用户控件绑定未返回正确的值

我制作了一个由 3 个滑块和一些标签组成的 UserControl。用于操纵类的平移、旋转和缩放值。

每个用户控件都有自己的平移、旋转和缩放属性。相应滑块的值绑定到此属性。

这一切都会正常工作,直到用户尝试通过用鼠标滑动滑块来手动更改该值。无论出于何种原因,这都不会更新该属性。

这是如何设置其中一个滑块的示例:

<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 个这样的用户控件组成。所以本质上总共有 147 个滑块。

让我们以第一个 UserControl 为例,它有这些值;
平移:3380
旋转:49972
比例:16807

如果我将翻译滑块移动到最大值(65535)并保存,我得到的返回值仍然是 3380。但是,如果我通过我添加的方法更新它们,它会按预期工作。只有当他们尝试手动滑动它时,它才会这样做。

除此之外,我还收到 51 条与 UserControls 相关的警告,但我不知道它们的含义。这是其中 2 个:

System.Windows.Data 警告:4:无法找到引用&ldquo;RelativeSource FindAncestor、AncestorType='System.Windows.Controls.ItemsControl&rdquo;、AncestorLevel='1'' 进行绑定的源。BindingExpression:Path=HorizontalContentAlignment; 数据项=空;目标元素是&ldquo;ListBoxItem&rdquo;(名称=&ldquo;&rdquo;);目标属性是&ldquo;HorizontalContentAlignment&rdquo;(类型&ldquo;HorizontalAlignment&rdquo;)

System.Windows.Data 警告:4:无法找到引用&ldquo;RelativeSource FindAncestor、AncestorType='System.Windows.Controls.ItemsControl&rdquo;、AncestorLevel='1'' 进行绑定的源。绑定表达式:路径=(0); 数据项=空;目标元素是&ldquo;ListBoxItem&rdquo;(名称=&ldquo;&rdquo;);目标属性是&ldquo;ClearTypeHint&rdquo;(类型&ldquo;ClearTypeHint&rdquo;),

我这整个绑定的事情做错了吗?我尝试在创建 UserControls 时将其添加到列表中,并设置 DataGrid 的 ItemsSource。


喵喵时光机
浏览 87回答 1
1回答

饮歌长啸

这里有太多错误,向您展示正确的代码比解释所有内容更容易。阅读我上面链接的文章并研究这段代码。我还没有完全按照我的方式重新设计:例如,没有主视图模型。现在这不是 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.xamlTripleSlider 的 XAML 基本上没问题;主要问题是它正在寻找以前不存在的视图模型。但我们还希望滑块值绑定在Value更改时更新绑定属性,而不是在滑块控件失去焦点时(这是不明显的默认行为)。因此,添加UpdateSourceTrigger=PropertyChanged到所有三个 Slider.Value 绑定。    Value="{Binding Path=Translation, UpdateSourceTrigger=PropertyChanged}" TripleSlider.xaml.cs就是这个。这就是该类应该的样子。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;    }}
打开App,查看更多内容
随时随地看视频慕课网APP