我有一个INotifyPropertyChanged
数据结构,它是用户控件的依赖项。其中一个数据结构的属性绑定到控件的一个元素。
MyData.cs(使用MVVM Light实现INotifyPropertyChanged
):
public class MyData : ObservableObject
{
private string _text;
public string Text
{
get { return _text; }
set { Set(() => Text, ref _text, value); }
}
public override string ToString()
{
return Text;
}
}
TextControl.xaml:
<UserControl x:Class="UITester.TextControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Name="This"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Text="{Binding ElementName=This, Path=Data.Text, Mode=TwoWay}"></TextBox>
</Grid>
</UserControl>
TextControl.xaml.cs:
public partial class TextControl : UserControl
{
public static MyData DefaultData = new MyData {Text = "Default"};
public MyData Data
{
get { return (MyData)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MyData), typeof(TextControl), new PropertyMetadata(DefaultData));
public TextControl()
{
InitializeComponent();
}
}
现在我想要的是依赖属性DataProperty
每当其中一个内部属性发生更改时就会更新(即通知任何感兴趣的人已经更改)。
我用两种方式测试:
通过将依赖项属性Data
绑定到Label
通过在MyControlText
中创建依赖项属性MainWindow
,并将控件的Data
和Label
绑定到它。
MainWindow.xaml:
<Window x:Class="UITester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:uiTester="clr-namespace:UITester"
Title="MainWindow" Height="350" Width="525"
x:Name="me">
<StackPanel DataContext="{Binding ElementName=me}">
<uiTester:TextControl x:Name="MyControl" Data="{Binding ElementName=me, Path=MyControlText}"></uiTester:TextControl>
<Label Content="{Binding ElementName=MyControl, Path=Data}"></Label>
<Label Content="{Binding ElementName=me, Path=MyControlText}"></Label>
<Button Click="SetButtonClick">Set</Button>
<Button Click="ResetButtonClick">Reset</Button>
</StackPanel>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private readonly Dictionary<int, int> _selected = new Dictionary<int, int>();
public MainWindow()
{
InitializeComponent();
//list.ItemsSource = _selected.Values;
}
private void SetButtonClick(object sender, RoutedEventArgs e)
{
MyControlText = new MyData{Text = "new"};
}
private void ResetButtonClick(object sender, RoutedEventArgs e)
{
MyControlText = null;
}
public MyData MyControlText
{
get { return (MyData)GetValue(MyControlTextProperty); }
set { SetValue(MyControlTextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyControlText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyControlTextProperty =
DependencyProperty.Register("MyControlText", typeof(MyData), typeof(MainWindow), new PropertyMetadata(new MyData{ Text = ""}));
}
当我更改控件中的TextBox
时,我希望依赖项属性Data
更新,因此我希望我的Label
都更新。
这不起作用,所以我尝试注册明确更改的内容:
public partial class TextControl : UserControl
{
public static MyData DefaultData = new MyData {Text = "Default"};
public MyData Data
{
get { return (MyData)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(MyData), typeof(TextControl), new PropertyMetadata(DefaultData) { PropertyChangedCallback = TextPropertyChanged });
private static void TextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (TextControl) d;
if (e.NewValue != null)
{
((MyData) e.NewValue).PropertyChanged += (s, ea) =>
{
c.GetBindingExpression(DataProperty).UpdateSource();
};
}
}
public TextControl()
{
InitializeComponent();
}
}
每当我设置一个新的依赖项属性时,我就会这样想,我将注册它的任何属性发生变化的事件。在处理程序中,我希望更新绑定到依赖项proeprty的所有元素。但这也不起作用......
我知道控件内部的绑定工作因为当我在UpdateSource
放置一个断点时代码确实中断了,但我不明白如何更新整个依赖属性(以及标签必然会这样。)
更新: 我也尝试调用'd.InvalidateProperty(DataProperty);'而不是'c.GetBindingExpression(DataProperty).UpdateSource();'但它也不起作用......
答案 0 :(得分:0)
使用MultiBinding可以让它部分工作。 这是一个更清晰的例子:
Data
有两个string
字段 - A
和B
。 string
的{{1}}表示形式是两个Data
的串联。string
允许使用转换器设置NotifyEditor
和A
数据。B' an propagates the change to the underlying
是DataConverter
,知道如何在两个IMultiValueConverter
和string
之间进行转换<强> Data.cs 强>:
Data
<强> NotifyEditor.xaml:强>
public class Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string a, b;
public string A
{
get { return a; }
set
{
a = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("A"));
}
}
public string B
{
get { return b; }
set
{
b = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("B"));
}
}
public override string ToString()
{
return string.Concat(A, B);
}
}
<强> NotifyEditor.xaml.cs:强>
<UserControl x:Class="SqlConnection.NotifyEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Me">
<StackPanel>
<TextBox Name="TextA"></TextBox>
<TextBox Name="TextB"></TextBox>
</StackPanel>
</UserControl>
<强> DataConverter.cs:强>
public partial class NotifyEditor : UserControl
{
public Data MyData
{
get { return (Data)GetValue(MyDataProperty); }
set { SetValue(MyDataProperty, value); }
}
// Using a DependencyProperty as the backing store for MyData. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDataProperty =
DependencyProperty.Register("MyData", typeof(Data), typeof(NotifyEditor), new PropertyMetadata(new Data()));
public NotifyEditor()
{
InitializeComponent();
var b = new MultiBinding
{
Converter = new DataConverter(),
Mode = BindingMode.TwoWay,
NotifyOnSourceUpdated = true,
NotifyOnTargetUpdated = true
};
b.Bindings.Add(new Binding("Text") { Source = TextA, Mode = BindingMode.TwoWay});
b.Bindings.Add(new Binding("Text") { Source = TextB, Mode = BindingMode.TwoWay });
SetBinding(MyDataProperty, b);
}
}
<强> MainWindow.xaml:强>
public class DataConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return new Data { A = values[0] as string, B = values[1] as string };
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
var d = value as Data;
if (d != null)
return new object[] { d.A, d.B };
return null;
}
}
<强> MainWindow.xaml.cs:强>
<Window x:Class="SqlConnection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SqlConnection"
Name="me"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:NotifyEditor x:Name="ne"></local:NotifyEditor>
<TextBox Text="{Binding ElementName=ne, Path=MyData, Mode=OneWay}"></TextBox>
<Button Click="SetClick">Set</Button>
</StackPanel>
</Window>
在我的测试中,我创建了public partial class MainWindow : Window
{
public Data Data
{
get { return (Data)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(Data), typeof(MainWindow), new PropertyMetadata(new Data()));
public MainWindow()
{
InitializeComponent();
}
private void SetClick(object sender, RoutedEventArgs e)
{
ne.MyData = new Data { A = "new A", B = "new B" };
}
}
,其中包含一个名为MainWindow
和NotifyEditor
的{{1}}绑定到控件的Label
。另外,我添加了一个按钮,为Content
设置Data
。这确实有效。当我更改Data
底部NotifyEditor
更新中的TextBox
时,当我单击按钮时,所有字段也会更新(因此转换器的两个方向都有效)。< / p>
这只是部分解决方案的原因是我仍然无法将NotifyEditor
s TextBox
绑定到NotifyEditor
的依赖项属性或视图模型的属性。如果我加上这个:
Data
到我的窗口(其中MainWindow
是 <local:NotifyEditor MyData="{Binding ElementName=me, Path=Data, Mode=TwoWay}"></local:NotifyEditor>
<Label Content="{Binding ElementName=me, Path=Data}"></Label>
<Button Click="SetMyDataClick">Set My Data</Button>
的名称,而me
是类型MainWindow
的依赖项属性),它不起作用。我认为通过设置绑定Data
,我在控件内定义的多重绑定被取消,然后数据的非传播方式应该传播。
如何保持我在控件中定义的多重绑定,但仍能从外部添加更多绑定?