簡體   English   中英

WPF〜綁定和INotifyPropertyChanged問題

[英]WPF ~ Trouble with Binding & INotifyPropertyChanged

WPF n00bie在這里,試圖使他的UI正常工作。

所以我做了這個測試例子。 綁定到HeaderText1的文本塊在應用程序啟動時會正確更改,但是綁定到HeaderText2的文本塊在單擊按鈕后不會更新。

我究竟做錯了什么? 提前致謝!!

<Window x:Class="DataBinding.DataContextSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataContextSample" Height="142.596" Width="310">
    <StackPanel Margin="15">
        <WrapPanel>
            <TextBlock Text="Window title:  " />
            <TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
            <Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
        </WrapPanel>
        <TextBlock Text="{Binding Path=DataContext.HeaderText}"></TextBlock>
        <TextBlock Text="{Binding Path=DataContext.HeaderText2}"></TextBlock>
    </StackPanel>
</Window>

主窗口類:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;


namespace DataBinding
{
    public partial class DataContextSample : Window
    {
        public string HeaderText { set; get; }



        public DataContextSample()
        {
            HeaderText = "YES";


            InitializeComponent();
            this.DataContext = this;

        }

        private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
        {

            BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
            binding.UpdateSource();

            Source source = new Source();
            source.HeaderText2 = "YES2";
        }
    }
}

還有INotifyPropertyChanged類

using System.ComponentModel;

namespace DataBinding
{
    public class Source : INotifyPropertyChanged
    {
        public string HeaderText2 { set; get; }

        public event PropertyChangedEventHandler PropertyChanged;


        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

    }
}

首先,您做錯了很多事情。

您不應該使用窗口,因為它是自己的數據上下文,應該設置一個視圖模型。

您不應在視圖中使用事件處理程序來操縱視圖模型。 您應該將按鈕綁定到命令。

您的源代碼似乎是“ viewmodel”,請考慮將其重命名為MainWindowViewModel(為清楚起見),然后執行此操作。

public class MainWindowViewModel : INotifyPropertyChanged
{
    private string headerText;
    private string headerText2;
    private ICommand updateHeaderText2;

    public string HeaderText
    {
        set
        {
            return this.headerText;
        }
        get 
        {
            this.headerText = value;

            // Actually raise the event when property changes
            this.OnPropertyChanged("HeaderText"); 
        }
    }

    public string HeaderText2
    {
        set
        {
            return this.headerText2;
        }
        get 
        {
            this.headerText2 = value;

            // Actually raise the event when property changes
            this.OnPropertyChanged("HeaderText2");
        }
    }

    public ICommand UpdateHeaderText2
    {
        get 
        {
            // Google some implementation for ICommand and add the MyCommand class to your solution.
            return new MyCommand (() => this.HeaderText2 = "YES2");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
}

並將此視圖模型設置為窗口的datacontext。

this.DataContext = new MainWindowViewModel();

然后在您的xaml中,您應該這樣綁定到viewmodel

<Window x:Class="DataBinding.DataContextSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DataContextSample" Height="142.596" Width="310">
    <StackPanel Margin="15">
        <WrapPanel>
            <TextBlock Text="Window title:  " />
            <!-- Not sure what this binding is? -->
            <TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
            <Button Name="btnUpdateSource" Command="{Binding UpdateHeaderText2}" Margin="5,0" Padding="5,0">*</Button>
        </WrapPanel>
        <TextBlock Text="{Binding HeaderText}"></TextBlock>
        <TextBlock Text="{Binding HeaderText2}"></TextBlock>
    </StackPanel>
</Window>

您將DataContext設置this (窗口)。 您在DataContext沒有名為HeaderText2的屬性,因此第二個綁定將不起作用。

我會這樣做(無需過多更改您的代碼,實際上我會使用適當的MVVM方法):

public partial class DataContextSample : Window
{
    public Source Source { get; set; }
    public string HeaderText { set; get; }

    public MainWindow()
    {
        InitializeComponent();

        HeaderText = "YES";
        Source = new Source { HeaderText2 = "YES" };
        DataContext = this;
    }

    private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
    {
        BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
        if (binding != null)
        {
            binding.UpdateSource();
        }

        Source.HeaderText2 = "YES2";
    }
}

我添加了一個名為Source的新屬性,該屬性的類型為Source 在構造函數中將其初始HeaderText2設置為相同的"YES" ,然后單擊按鈕將其更改為"YES2"

您還必須更改Source類,以實際通知更改:

public class Source : INotifyPropertyChanged
{
    private string _headerText2;

    public string HeaderText2
    {
        get { return _headerText2; }
        set
        {
            _headerText2 = value;
            OnPropertyChanged("HeaderText2");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
}

然后在您的XAML中:

<StackPanel Margin="15">
    <WrapPanel>
        <TextBlock Text="Window title:  " />
        <TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
        <Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
    </WrapPanel>
    <TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
    <TextBlock Text="{Binding Path=Source.HeaderText2}"></TextBlock>
</StackPanel>

好的,您的代碼存在一些問題。 首先,您永遠不會將“源”分配給數據上下文,因此第二個TextBlock無法找到“ HeaderText2”的值。

但是,如果您將“源”分配給文本塊數據上下文,那么我們可以獲取“ HeaderText2”的值。 考慮下面的代碼

<Window x:Class="DataBinding.DataContextSample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="DataContextSample" Height="142.596" Width="310">
    <StackPanel Margin="15">
        <WrapPanel>
            <TextBlock Text="Window title:  " />
            <TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
            <Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
        </WrapPanel>
        <TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
        <TextBlock Name="TextBlock2" Text="{Binding Path=HeaderText2}"></TextBlock>
    </StackPanel>
</Window>

我們為您的第二個Textblock命名為“ TextBlock2”,並從綁定中刪除了“ Datacontext”部分。

然后,我們將“源”對象的創建從按鈕事件移到了Windows構造函數中(當我們要做的只是更新屬性時,無需每次單擊按鈕都創建一個新對象)

public partial class DataContextSample : Window
{
    public string HeaderText { set; get; }
    private Source source { get; set; }

    public DataContextSample()
    {
        ...

        source = new Source();
        TextBlock2.DataContext = source;

        ...
    }

    ...
}

然后在您的按鈕單擊事件中,我們為您的數據綁定屬性分配一個值“ YES2”。

private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
    ...

    source.HeaderText2 = "YES2";
}

但是,還有更多細節。 您的類“ Source”確實實現了“ INotifyPropertyChanged”,但從未“使用”它。 我的意思是,當您為屬性“ HeaderText2”分配值時,您實際上從未“通知” UI發生了某些更改,因此UI不會獲取新值。 考慮下面的代碼:

public class Source : INotifyPropertyChanged
{        
    public string HeaderText2 { set
        {
            headerText2 = value;
            OnPropertyChanged("HeaderText2");
        }
        get
        {
            return headerText2;
        }
    }
    string headerText2;

    ...
}

因此,讓我們看一下對屬性“ HeaderText2”所做的工作。 每當“ HeaderText2”獲得分配的值時,它將首先將該值保存在privat屬性中(以便我們以后可以從中讀取)。 但是除此之外,我們還使用屬性名稱調用“ OnPropertyChanged”方法。 該方法將依次檢查是否有人“監聽”了我們的“ PropertyChanged”事件(並且由於我們對當前對象進行了數據綁定,因此有人正在監聽),並創建了一個新事件。

現在,我們已經為您的文本塊分配了一個帶有“ HeaderText2”路徑的數據源,當我們在數據源上更新“ HeaderText2”並且在按鈕click事件上更新“ HeaderText2”時,我們將通知所有偵聽器。

編碼愉快!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM