UserControl 绑定未返回正确的值
UserControl Binding Not Returning Correct Value
我制作了一个由 3 个滑块和一些标签组成的 UserControl。用于操纵 class.
的平移、旋转和缩放值
每个 UserControl 都有自己的平移、旋转和缩放 属性。对应滑块的Value绑定到这个属性.
在用户尝试通过使用鼠标滑动滑块来手动更改值之前,这一切都正常运行。无论出于何种原因,这都不会更新 属性.
这是其中一个滑块的设置示例:
<Slider x:Name="sliderTranslation" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" ToolTip="{Binding Value, RelativeSource={RelativeSource Self}}" Value="{Binding Path=Translation}" Thumb.DragCompleted="SliderTranslation_DragCompleted" Maximum="65535" TickFrequency="0" SmallChange="1" AutoToolTipPlacement="TopLeft"/>
这就是我的 DataGrid 的设置方式:
<DataGrid x:Name="dgValueList" Margin="10,72,10,76" SelectionMode="Single" IsReadOnly="True" BorderThickness="2" AlternationCount="2" EnableRowVirtualization="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Face Values" Width="*" CanUserReorder="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:FaceValueSlider/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
所以对于某些上下文。 DataGrid 由 49 个这样的 UserControl 组成。所以基本上总共有 147 个滑块。
让我们以第一个 UserControl 为例,它有这些值;
翻译:3380
旋转:49972
规模:16807
如果我将翻译滑块移动到最大值 65535 并保存,我得到的返回值仍然是 3380。但是,如果我通过我添加的方法更新它们,它会按预期工作。只有当他们尝试手动滑动它时才会这样做。
除此之外,我还收到 51 条与用户控件相关的警告,我不知道它们是什么意思。这是其中的 2 个:
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'ClearTypeHint' (type 'ClearTypeHint'),
我是不是做错了整个绑定的事情?我已经尝试将 UserControls 添加到列表中,而不是在它们创建时添加并设置 DataGrid 的 ItemsSource。
但最终看起来像这样。
这里有一个 MVVM example 可以帮助您入门。阅读并理解那篇文章,了解此处的基本操作原理。使用它来获取以下代码的 ObservableObject
基础 class。
这里有很多错误,向您展示更正后的代码比解释所有内容更容易。阅读我上面链接的文章并研究这段代码。我还没有按照我的方式完全重新设计:例如,没有主视图模型。这现在不是 MVVM 的一个很好的例子,但它说明了你在网格中放置什么样的东西,如何编写模板列,以及如何正确更新属性。
首先,您将用户控件的实例用作同一控件的视图模型,但它不是真正的视图模型,因为它从不引发任何 属性 更改通知。让我们为网格编写一个实际的项目视图模型。这不是 UI 控件。是数据,会在UI控件中显示。它有信息,当信息发生变化时它会收到通知。如果你愿意,它也可以有一些逻辑。
public class SliderItem : ObservableObject
{
public SliderItem()
{
}
public SliderItem(int trans, int rot, int scale)
{
Translation = trans;
Rotation = rot;
Scale = scale;
}
public void UpdateValues(int newTrans, int newRot, int newScale)
{
Translation = newTrans;
Rotation = newRot;
Scale = newScale;
}
public void UpdateDescription(string newText)
{
if(!String.IsNullOrWhiteSpace(newText))
{
Description = newText;
OnPropertyChanged(nameof(Description));
}
}
public String Description { get; private set; }
private int _translation = 0;
public int Translation
{
get { return _translation; }
set
{
if (value != _translation)
{
_translation = value;
OnPropertyChanged(nameof(Translation));
}
}
}
private int _rotation = 0;
public int Rotation
{
get { return _rotation; }
set
{
if (value != _rotation)
{
_rotation = value;
OnPropertyChanged(nameof(Rotation));
}
}
}
private int _scale = 0;
public int Scale
{
get { return _scale; }
set
{
if (value != _scale)
{
_scale = value;
OnPropertyChanged(nameof(Scale));
}
}
}
}
TripleSlider.xaml
您的 TripleSlider XAML 基本没问题;主要问题是它正在寻找以前不存在的视图模型。但我们还希望滑块值绑定在 Value
更改时更新绑定属性,而不是在 Slider 控件失去焦点时(这是 non-obvious 默认行为)。因此,将 UpdateSourceTrigger=PropertyChanged
添加到 所有三个 Slider.Value 绑定 。
Value="{Binding Path=Translation, UpdateSourceTrigger=PropertyChanged}"
TripleSlider.xaml.cs
就是这样。这就是 class 应该的样子。
public partial class TripleSlider : UserControl
{
public TripleSlider()
{
InitializeComponent();
}
}
MainWindow.xaml 没问题。 MainWindow.xaml.cs 有点变化:
public partial class MainWindow : Window
{
// Don't use arrays. Use ObservableCollection<WhateverClass> for binding to UI controls,
// use List<Whatever> for anything else.
private ObservableCollection<SliderItem> _sliders = new ObservableCollection<SliderItem>();
public MainWindow()
{
InitializeComponent();
// The ObservableCollection will notify the grid when you add or remove items
// from the collection. Set this and forget it. Everywhere else, interact with
// _sliders, and let the DataGrid handle its end by itself.
// Also get rid of EnableRowVirtualization="False" from the DataGrid. Let it
// virtualize.
myDataGrid.ItemsSource = _sliders;
}
private void BAddControls_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 49; i++)
{
var newSlider = new SliderItem();
newSlider.UpdateDescription(String.Format("{0}: Unkown Value", (i+1)));
newSlider.UpdateValues((i+1)*1337, (i+1)*1337, (i+1)*1337);
_sliders.Add(newSlider);
}
bAddControls.IsEnabled = false;
}
private void BFetchValues_Click(object sender, RoutedEventArgs e)
{
if (myDataGrid.SelectedItem != null)
{
var selectedSlider = myDataGrid.SelectedItem as SliderItem;
MessageBox.Show(String.Format("Translation: {0}\nRotation: {1}\nScale: {2}", selectedSlider.Translation, selectedSlider.Rotation, selectedSlider.Scale), "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void MyDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
bFetchValues.IsEnabled = (myDataGrid.SelectedItem != null) ? true : false;
}
}
我制作了一个由 3 个滑块和一些标签组成的 UserControl。用于操纵 class.
的平移、旋转和缩放值每个 UserControl 都有自己的平移、旋转和缩放 属性。对应滑块的Value绑定到这个属性.
在用户尝试通过使用鼠标滑动滑块来手动更改值之前,这一切都正常运行。无论出于何种原因,这都不会更新 属性.
这是其中一个滑块的设置示例:
<Slider x:Name="sliderTranslation" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center" ToolTip="{Binding Value, RelativeSource={RelativeSource Self}}" Value="{Binding Path=Translation}" Thumb.DragCompleted="SliderTranslation_DragCompleted" Maximum="65535" TickFrequency="0" SmallChange="1" AutoToolTipPlacement="TopLeft"/>
这就是我的 DataGrid 的设置方式:
<DataGrid x:Name="dgValueList" Margin="10,72,10,76" SelectionMode="Single" IsReadOnly="True" BorderThickness="2" AlternationCount="2" EnableRowVirtualization="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Face Values" Width="*" CanUserReorder="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:FaceValueSlider/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
所以对于某些上下文。 DataGrid 由 49 个这样的 UserControl 组成。所以基本上总共有 147 个滑块。
让我们以第一个 UserControl 为例,它有这些值;
翻译:3380
旋转:49972
规模:16807
如果我将翻译滑块移动到最大值 65535 并保存,我得到的返回值仍然是 3380。但是,如果我通过我添加的方法更新它们,它会按预期工作。只有当他们尝试手动滑动它时才会这样做。
除此之外,我还收到 51 条与用户控件相关的警告,我不知道它们是什么意思。这是其中的 2 个:
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'ClearTypeHint' (type 'ClearTypeHint'),
我是不是做错了整个绑定的事情?我已经尝试将 UserControls 添加到列表中,而不是在它们创建时添加并设置 DataGrid 的 ItemsSource。
但最终看起来像这样
这里有一个 MVVM example 可以帮助您入门。阅读并理解那篇文章,了解此处的基本操作原理。使用它来获取以下代码的 ObservableObject
基础 class。
这里有很多错误,向您展示更正后的代码比解释所有内容更容易。阅读我上面链接的文章并研究这段代码。我还没有按照我的方式完全重新设计:例如,没有主视图模型。这现在不是 MVVM 的一个很好的例子,但它说明了你在网格中放置什么样的东西,如何编写模板列,以及如何正确更新属性。
首先,您将用户控件的实例用作同一控件的视图模型,但它不是真正的视图模型,因为它从不引发任何 属性 更改通知。让我们为网格编写一个实际的项目视图模型。这不是 UI 控件。是数据,会在UI控件中显示。它有信息,当信息发生变化时它会收到通知。如果你愿意,它也可以有一些逻辑。
public class SliderItem : ObservableObject
{
public SliderItem()
{
}
public SliderItem(int trans, int rot, int scale)
{
Translation = trans;
Rotation = rot;
Scale = scale;
}
public void UpdateValues(int newTrans, int newRot, int newScale)
{
Translation = newTrans;
Rotation = newRot;
Scale = newScale;
}
public void UpdateDescription(string newText)
{
if(!String.IsNullOrWhiteSpace(newText))
{
Description = newText;
OnPropertyChanged(nameof(Description));
}
}
public String Description { get; private set; }
private int _translation = 0;
public int Translation
{
get { return _translation; }
set
{
if (value != _translation)
{
_translation = value;
OnPropertyChanged(nameof(Translation));
}
}
}
private int _rotation = 0;
public int Rotation
{
get { return _rotation; }
set
{
if (value != _rotation)
{
_rotation = value;
OnPropertyChanged(nameof(Rotation));
}
}
}
private int _scale = 0;
public int Scale
{
get { return _scale; }
set
{
if (value != _scale)
{
_scale = value;
OnPropertyChanged(nameof(Scale));
}
}
}
}
TripleSlider.xaml
您的 TripleSlider XAML 基本没问题;主要问题是它正在寻找以前不存在的视图模型。但我们还希望滑块值绑定在 Value
更改时更新绑定属性,而不是在 Slider 控件失去焦点时(这是 non-obvious 默认行为)。因此,将 UpdateSourceTrigger=PropertyChanged
添加到 所有三个 Slider.Value 绑定 。
Value="{Binding Path=Translation, UpdateSourceTrigger=PropertyChanged}"
TripleSlider.xaml.cs
就是这样。这就是 class 应该的样子。
public partial class TripleSlider : UserControl
{
public TripleSlider()
{
InitializeComponent();
}
}
MainWindow.xaml 没问题。 MainWindow.xaml.cs 有点变化:
public partial class MainWindow : Window
{
// Don't use arrays. Use ObservableCollection<WhateverClass> for binding to UI controls,
// use List<Whatever> for anything else.
private ObservableCollection<SliderItem> _sliders = new ObservableCollection<SliderItem>();
public MainWindow()
{
InitializeComponent();
// The ObservableCollection will notify the grid when you add or remove items
// from the collection. Set this and forget it. Everywhere else, interact with
// _sliders, and let the DataGrid handle its end by itself.
// Also get rid of EnableRowVirtualization="False" from the DataGrid. Let it
// virtualize.
myDataGrid.ItemsSource = _sliders;
}
private void BAddControls_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 49; i++)
{
var newSlider = new SliderItem();
newSlider.UpdateDescription(String.Format("{0}: Unkown Value", (i+1)));
newSlider.UpdateValues((i+1)*1337, (i+1)*1337, (i+1)*1337);
_sliders.Add(newSlider);
}
bAddControls.IsEnabled = false;
}
private void BFetchValues_Click(object sender, RoutedEventArgs e)
{
if (myDataGrid.SelectedItem != null)
{
var selectedSlider = myDataGrid.SelectedItem as SliderItem;
MessageBox.Show(String.Format("Translation: {0}\nRotation: {1}\nScale: {2}", selectedSlider.Translation, selectedSlider.Rotation, selectedSlider.Scale), "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void MyDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
bFetchValues.IsEnabled = (myDataGrid.SelectedItem != null) ? true : false;
}
}