수색…


통사론

  • <TextBlock Text="{Binding Title}"/>

  • <TextBlock Text="{Binding Path=Title}"/>

  • <TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>

비고

이러한 모든 태그는 동일한 결과를 생성합니다.

문자열을 Text 속성에 바인딩

런타임에 UI 내용을 변경하려면 Binding 을 사용할 수 있습니다. 바인딩 된 속성이 코드에서 변경되면 UI에 표시됩니다.

<TextBlock Text="{Binding Title}"/>

UI에 변경 사항을 알리려면 속성이 INotifyPropertyChanged 인터페이스에서 PropertyChanged 이벤트를 발생 시키거나 Dependency Property 사용해야합니다.

"제목"속성이 xaml.cs 파일 또는 XAML 의 Datacontext 클래스에 있으면 바인딩이 작동합니다.

Datacontext는 XAML에서 직접 설정할 수 있습니다.

<Window x:Class="Application.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Application">
<Window.DataContext>
   <local:DataContextClass/>
</Window.DataContext>

문자열 바인딩 서식 지정

예를 들어 날짜와 같은 무언가의 바인딩을 만들 때 특정 형식으로 코드에 숨기지 않고 표시 할 수 있습니다.

이렇게하려면 StringFormat 속성을 사용할 수 있습니다.

여기 예시들이 있습니다 :

Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"

내 날짜 형식은 다음과 같습니다.

2016 년 8 월 16 일 화요일


다음은 온도의 또 다른 예입니다.

Text="{Binding Path=Temp, StringFormat={}{0}°C}"

이 형식은 다음과 같습니다.

25 ° C

INotifyPropertyChanged의 기본 사항

정적 객체 만 표시하고 상호 관련 객체의 변경 사항에 UI가 응답하도록하려면 INotifyPropertyChanged 인터페이스의 기본 사항을 이해해야합니다.

MainWindow 다음과 같이 정의했다고 가정합니다.

<Window x:Class="Application.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:vm="clr-namespace:Application.ViewModels>
    <Window.DataContext>
       <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <TextBlock Text={Binding Path=ApplicationStateText}" />
    </Grid>
</Window>

우리의 Viewmodel-Class MainWindowViewModel 다음과 같이 정의했다.

namespace Application.ViewModels
{
    public class MainWindowViewModel
    {
        private string _applicationStateText;

        public string ApplicationStateText
        {
            get { return _applicationStateText; }
            set { _applicationStateText = value; }
        }
        public MainWindowViewModel() 
        { 
            ApplicationStateText = "Hello World!";
        }

    }
}

응용 프로그램의 TextBlock은 바인딩으로 인해 텍스트 Hello World를 표시합니다. 런타임 동안 ApplicationStateText가 변경되면 UI에 이러한 변경 사항이 통지되지 않습니다.
이것을 구현하기 위해 우리의 DataSource는이 경우 MainWindowViewModel 인터페이스 INotifyPropertyChanged 를 구현해야합니다. 이것에 의해, BindingsPropertyChangedEvent 에 등록 할 수있게됩니다.
ApplicationStateText 속성을 다음과 같이 변경할 때마다 PropertyChangedEventHandler 를 호출하면됩니다.

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Application.ViewModels
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged( [CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private string _applicationStateText;

        public string ApplicationStateText
        {
            get { return _applicationStateText; }
            set
            {
                if (_applicationStateText != value)
                {
                    _applicationStateText = value;
                    NotifyPropertyChanged();
                }
            }
        }
        public MainWindowViewModel()
        {
            ApplicationStateText = "Hello World!";
        }
    }
}

TextBlock.TextBinding 이 실제로 PropertyChangedEvent 수신하는지 확인하십시오.

<Window x:Class="Application.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:vm="clr-namespace:Application.ViewModels">
    <Window.DataContext>
       <vm:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <TextBlock Text={Binding Path=ApplicationStateText, UpdateSourceTrigger=PropertyChanged }" />
    </Grid>
</Window>

INotifyPropertyChanged 및 INotifyCollectionChanged를 사용하여 개체 컬렉션에 바인딩

User 객체의 Properties가 프로그램 적으로 업데이트 될 수있는 ViewModelUsers 속성 아래에 나열된 모든 User 객체를 표시하기로되어있는 ListView 가 있다고 가정합니다.

<ListView ItemsSource="{Binding Path=Users}" >
    <ListView.ItemTemplate>
        <DataTemplate DataType="{x:Type models:User}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="5,3,15,3" 
                         Text="{Binding Id, Mode=OneWay}" />
                <TextBox Width="200"
                         Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=450}"/>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

User 객체에 대해 올바르게 구현 된 INotifyPropertyChanged Beeing에도 불구하고

public class User : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _id;
    private string _name;

    public int Id
    {
        get { return _id; }
        private set
        {
            if (_id == value) return;
            _id = value;
            NotifyPropertyChanged();
        }
    }
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            NotifyPropertyChanged();
        }
    }

    public User(int id, string name)
    {
        Id = id;
        Name = name;
    }

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ViewModel 객체의 경우

public sealed class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private List<User> _users;
    public List<User> Users
    {
        get { return _users; }
        set
        {
            if (_users == value) return;
            _users = value;
            NotifyPropertyChanged();
        }
    }
    public MainWindowViewModel()
    {
        Users = new List<User> {new User(1, "John Doe"), new User(2, "Jane Doe"), new User(3, "Foo Bar")};
    }

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

사용자에 대한 변경이 프로그래밍 방식으로 이루어진 경우 UI가 업데이트되지 않습니다.

이는 List 인스턴스 자체에 INotifyPropertyChanged 만 설정했기 때문입니다. Element의 한 속성이 변경되면 List가 완전히 다시 인스턴스화 된 경우에만 UI가 업데이트됩니다.

// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);

그러나 이것은 매우 귀찮고 믿을 수 없을만큼 성능이 나쁘다.
사용자의 ID와 이름을 모두 보여주는 100,000 개의 요소 목록이있는 경우 각 요소를 다시 작성해야하는 200,000 개의 데이터 바인딩이 있습니다. 이로 인해 변경 사항이있을 때마다 사용자에게 지연 될 수 있습니다.

이 문제를 부분적으로 해결하려면 List<T> 대신 System.ComponentModel.ObservableCollection<T> 사용할 수 있습니다.

private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
    get { return _users; }
    set
    {
        if (_users == value) return;
        _users = value;
        NotifyPropertyChanged();
    }
}

ObservableCollectionCollectionChanged 이벤트를 제공하고 INotifyPropertyChanged 자체를 구현합니다. MSDN 에 따르면 이벤트는 "[..] 항목이 추가 , 제거 , 변경 , 이동 되거나 전체 목록이 새로 고쳐질 때"상승합니다.
.NET 4.5.2 이전 버전에서는 ObservableCollection 이 Collection ObservableCollection Event를 발생시키지 않을 입니다.

다음은 솔루션을 우리는 단순히 우리 자신의 구현할 수 TrulyObservableCollection<T> 포함하지 않는 INotifyPropertyChanged 대한 제약 T 우리가 필요로하는 모든 것을 갖고 어떠했는지를 노출 T 구현 INotifyPropertyChanged 여부 :

/*
 * Original Class by Simon @StackOverflow http://stackoverflow.com/a/5256827/3766034
 * Removal of the INPC-Constraint by Jirajha @StackOverflow 
 * according to to suggestion of nikeee @StackOverflow http://stackoverflow.com/a/10718451/3766034
 */
public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
{
    private readonly bool _inpcHookup;
    public bool NotifyPropertyChangedHookup => _inpcHookup;

    public TrulyObservableCollection()
    {
        CollectionChanged += TrulyObservableCollectionChanged;
        _inpcHookup = typeof(INotifyPropertyChanged).GetTypeInfo().IsAssignableFrom(typeof(T));
    }
    public TrulyObservableCollection(IEnumerable<T> items) : this()
    {
        foreach (var item in items)
        {
            this.Add(item);
        }
    }

    private void TrulyObservableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (NotifyPropertyChangedHookup && e.NewItems != null && e.NewItems.Count > 0)
        {
            foreach (INotifyPropertyChanged item in e.NewItems)
            {
                item.PropertyChanged += ItemPropertyChanged;
            }
        }
        if (NotifyPropertyChangedHookup && e.OldItems != null && e.OldItems.Count > 0)
        {
            foreach (INotifyPropertyChanged item in e.OldItems)
            {
                item.PropertyChanged -= ItemPropertyChanged;
            }
        }
    }
    private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
        OnCollectionChanged(args);
    }
}

우리의 ViewModel에서 Property UsersTrulyObservableCollection<User> 로 정의하십시오

private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
    get { return _users; }
    set
    {
        if (_users == value) return;
        _users = value;
        NotifyPropertyChanged();
    }
}

이제 모든 단일 Binding 을 다시 만들 필요없이 컬렉션 내의 요소에 대한 INPC-Property가 변경되면 UI에 알림이 표시됩니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow