为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
喵喵时光机 2023-07-09 16:34:29
我制作了一个由 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。
查看完整描述

1 回答

?
饮歌长啸

TA贡献1951条经验 获得超3个赞

这里有太多错误,向您展示正确的代码比解释所有内容更容易。阅读我上面链接的文章并研究这段代码。我还没有完全按照我的方式重新设计:例如,没有主视图模型。现在这不是 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更改时更新绑定属性,而不是在滑块控件失去焦点时(这是不明显的默认行为)。因此,添加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;

    }

}


查看完整回答
反对 回复 2023-07-09
  • 1 回答
  • 0 关注
  • 86 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信