WPF 强制重新绑定

14

我有一个对象,不能继承DependencyObject或使用NotifyPropertyChanged,并且我已经将其绑定到相当多的控件上,所以当属性更改时,我不想在代码中去每个控件并更改它的值。因此,我认为必须有一种方法可以告诉XAML用一两行代码“重新绑定”所有已绑定的内容,而不是一个一个地去处理:

label1.Content = myObject.DontNotifyThis;
label2.Content = myObject.DontNotifyThisEither;
label3.Content = myObject.DontEvenThinkOfNotifyingThis;
label4.Content = myObject.NotSoFastPal;

如此等等...

这是一个过于简化的例子:

XAML:

<Window x:Class="StackOverflowTests.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" x:Name="window1" Height="300" Width="300" Loaded="window1_Loaded">
    <Grid x:Name="gridMain">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Content="{Binding Status}" ContentStringFormat="Today's weather: {0}" />
        <Label Grid.Row="2" Content="{Binding Temperature}" ContentStringFormat="Today's temperature: {0}" />
        <Label Grid.Row="1" Content="{Binding Humidity}" ContentStringFormat="Today's humidity: {0}" />
    </Grid>
</Window>

C#:

using System.Windows;

namespace StackOverflowTests
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        Weather weather = new Weather("Cloudy", "60F", "25%");

        public Window1()
        {
            InitializeComponent();
            this.DataContext = weather;
        }

        private void window1_Loaded(object sender, RoutedEventArgs e)
        {
            weather.Status = "Sunny";
            weather.Temperature = "80F";
            weather.Humidity = "3%";
        }       
    }

    class Weather
    {
        public string Status { get; set; }
        public string Temperature { get; set; }
        public string Humidity { get; set; }

        public Weather(string status, string temperature, string humidity)
        {
            this.Status = status;
            this.Temperature = temperature;
            this.Humidity = humidity;
        }
    }
}

我找到了一种方法,但并不十分优雅,不幸的是,我不能只将DataContext设置为weather的新实例,它需要是相同的引用(这就是为什么我将其设置为null以进行更改的原因):

private void window1_Loaded(object sender, RoutedEventArgs e)
{
    weather.Status = "Sunny";
    weather.Temperature = "80F";
    weather.Humidity = "3%";

    // bad way to do it
    Weather w = (Weather)this.DataContext;
    this.DataContext = null;
    this.DataContext = w;
}   

提前致谢!


好奇:为什么你不能实现INPC? - Kent Boogaart
我们的应用程序正在使用Undo/Redo,INotifyPropertyChanging序列化对象的先前状态,INotifyPropertyChanged使对象保存到新的XmlSerialized文件中。然而,我需要更改的这些特定属性不会改变对象的保存状态(不会更改字体、颜色、背景、边框)或用户想要保存的任何内容。如果我使用这些属性进行NotifyPropertyChanging / Changed,系统将认为对象已更改,但对于用户来说,它并没有更改。这就是为什么我不能使用它的原因。 - Carlo
我理解你的意思,但这听起来对我来说是一个有缺陷的设计。你最好使用INPC作为通用属性更改通知,然后再使用另一种机制来跟踪您关心撤消/重做的状态更改。也许现在改变你的设计已经太晚了,所以我明白你的观点。 - Kent Boogaart
1
另一个需要注意的是,MSDN创建依赖属性的示例是不完整的。我因为这篇博客文章得救了:http://geekswithblogs.net/thibbard/archive/2008/04/22/wpf-custom-control-dependency-property-gotcha.aspx - sinelaw
1
为什么这是一种不好的方式?我喜欢它。它可以避免我将所有属性重写为dp,并使我的代码简单化。我只需要在对话框中添加一个重置按钮,以恢复所有控件的默认值,这真的让我很开心。 - pasx
2个回答

22

如果您可以访问要更新绑定的元素,则可以显式地更新绑定。 您可以检索元素上的Binding Expression,然后使用UpdateTarget()刷新UI,或者使用UpdateSource刷新后备属性(如果您想将其绑定到可编辑的内容,例如TextBox)。

这里是一个简单的示例:

<StackPanel>
    <TextBlock x:Name="uiTextBlock" Text="{Binding MyString}" />
    <Button Click="Button_Click"
            Content="Rebind" />
</StackPanel>

public partial class Window1 : Window
{
    public string MyString { get; set; }

    public Window1()
    {
        MyString = "New Value";

        InitializeComponent();
        this.DataContext = this;
    }
    int count = 0;
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MyString = "Rebound " + ++count + " times";

        var bindingExpression = uiTextBlock.GetBindingExpression(TextBlock.TextProperty);
        bindingExpression.UpdateTarget();
    }
}

我建议尽可能使用INotifyPropertyChanged。这样,您可以将逻辑从代码后台中提取出来。


好的,这个可以运行,唯一的问题是,我需要为每个需要更新的控件都这样做吗? - Carlo
如果你不能实现 INotifyPropertyChanged,那么可以创建一个所需 BindingExpressions 集合,然后使用 LINQ 扩展 .ForEach(b => b.UpdateTarget())。但如果需要更新很多东西,或者一次只能访问某些控件,那么像你已经发现的那样“循环” DataContext 可能更简单。 - rmoore
我认为更新每个控件是可行的,唯一让我不喜欢这种方法的是,如果我添加一个新的控件,我也必须在代码中添加它以更新其目标。但这不是很大的问题。谢谢! - Carlo

0

当你想更新一大堆绑定表达式时,这样做很丑陋。一个更好的方法是影子复制:

public partial class MainWindow : Window
{
    Weather weather = new Weather("Cloudy", "60F", "25%");
    public MainWindow()
    {
        InitializeComponent();

        
        this.DataContext = weather;
        this.Loaded += delegate
        {
            weather.Status = "Sunny";
            weather.Temperature = "80F";
            weather.Humidity = "3%";

            
            this.DataContext = weather.ShadowCopy();
        };
    }
    
    class Weather
    {
        public string Status { get; set; }
        public string Temperature { get; set; }
        public string Humidity { get; set; }

        public Weather(string status, string temperature, string humidity)
        {
            this.Status = status;
            this.Temperature = temperature;
            this.Humidity = humidity;
        }

        public Weather ShadowCopy() => this.MemberwiseClone() as Weather;
    }
}

第二种方法是使用包装器,例如:

    <Label Grid.Row="0" Content="{Binding Weather.Status}" ContentStringFormat="Today's weather: {0}" />
    <Label Grid.Row="2" Content="{Binding Weather.Temperature}" ContentStringFormat="Today's temperature: {0}" />
    <Label Grid.Row="1" Content="{Binding Weather.Humidity}" ContentStringFormat="Today's humidity: {0}" />

    public MainWindow()
    {
        InitializeComponent();


        this.DataContext = new DataSource { Weather = weather };
        this.Loaded += delegate
        {
            weather.Status = "Sunny";
            weather.Temperature = "80F";
            weather.Humidity = "3%";


            this.DataContext = new DataSource { Weather = weather };
        };
    }


    class DataSource
    {
        public Weather Weather { get; set; }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接