【发布时间】:2016-02-05 19:39:59
【问题描述】:
我在更新 WPF 中的绑定数据时遇到了严重问题。 基本上,我的应用中有一些数据,这些数据在属性更改时不会更新,我无法修复它。
在我的应用程序中,我尝试使用 MVVM 模式,因此有一个具有 X、Y 属性的模型:
public class Сross : INotifyPropertyChanged
{
private double x;
private double y;
public double X
{
get { return x; }
set
{
x = value;
RaisePropertyChanged("X");
}
}
public double Y
{
get { return y; }
set
{
y = value;
RaisePropertyChanged("Y");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
...有一个 ViewModel,其中包含一个 ObservableCollection of Crosses:
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
Crosses = new ObservableCollection<Cross>()
{
new Cross ()
{
X = 300,
Y = 200
},
new Cross ()
{
X = 400,
Y = 300
}
};
private ObservableCollection<Cross> crosses;
public ObservableCollection<Cross> Crosses
{
get
{
return crosses;
}
set
{
crosses = value;
RaisePropertyChanged("Crosses");
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...还有一个 View,其中有一个 ListBox,数据绑定到这个 ObservableCollection of Crosses。
<Page x:Class="CharMaker.App.MainPageView">
<Page.DataContext>
<local:MainWindowViewModel />
</Page.DataContext>
....
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" Background="#01FFFFFF" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemsSource>
<Binding Path="Crosses" Mode="TwoWay" />
</ListBox.ItemsSource>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}" />
<Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Page>
此代码的工作原理是,当您在 ListBox 字段中移动 Cross 项(具有基于 Thumb 控件的 DataTemplate)时,它的 Canvas.Left(或 Top)正在发生变化,它会更新 Cross 的 X 和 Y 属性项目,反之亦然。
<DataTemplate DataType="{x:Type model:Cross}">
<Thumb DragDelta="Thumb_DragDelta"
IsEnabled="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Canvas Margin="0">
<Line X1="-5" X2="5" Y1="-5" Y2="5" Stroke="#FF2E61AB" StrokeThickness="1.5"
x:Name="FirstLine" />
<Line X1="-5" X2="5" Y1="5" Y2="-5" Stroke="#FF2E61AB" StrokeThickness="1.5"
x:Name="SecondLine" />
</Canvas>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter TargetName="FirstLine" Property="Stroke" Value="Red"/>
<Setter TargetName="SecondLine" Property="Stroke" Value="Red"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</DataTemplate>
问题在于,在加载列表框后(并且十字架出现在屏幕上的适当位置),当我移动它们时,它们不会更改 ViewModel 中的 X 和 Y 属性。如果我在 ObservableCollection Crosses 中更改 X 和 Y,它们不会在屏幕上移动。 值得一提的是,我做了一个带有 TextBoxes 的 ListBox 进行测试,它也绑定了 Crosses 集合的 X、Y 属性:
<ListBox Grid.Row="2" Grid.ColumnSpan="4" ItemsSource="{Binding Crosses}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type model:Cross}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding X, Mode=TwoWay}" />
<TextBox Grid.Column="1" Text="{Binding Y, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
有趣的是,在这个 ListBox 中,数据绑定工作得很好! 当我移动十字时,TextBox 中的值会发生变化,而当 TextBox 中的值发生变化时,十字会移动到位置。
但在同一时间(如果我暂停应用程序),我看到 ViewModel 中的 Observable Collection Crosses 中的值与第一次加载时的值相同。
请帮我找出问题所在! 提前谢谢!
【问题讨论】:
-
当 X,Y 改变时,调试视图会显示什么?视图中是否发现任何绑定错误?
-
你的十字架数据模板是什么样的?
-
@JeongKim 调试视图显示,即使在移动十字架之后,X 和 Y 仍然是初始化期间加载的值。但是在屏幕上我看到它们被移动了,带有坐标的文本框显示了实际值。
-
@Peter 我在描述中添加了 DataTemplate
-
@Lark3D 奇怪的是它在这里工作得很好(在主窗口上移动十字架会反射回收藏中的十字架项目,反之亦然)。
Thumb_DragDelta很简单: var t = sender as Thumb; if (t != null) { var c = t.DataContext as Cross; if (c != null) { c.X += e.HorizontalChange; c.Y += e.VerticalChange; }