【问题标题】:Binding a struct property on a ViewModel to a WPF toolkit PropertyGrid does not update ViewModel when UI values change当 UI 值更改时,将 ViewModel 上的结构属性绑定到 WPF 工具包 PropertyGrid 不会更新 ViewModel
【发布时间】:2018-06-06 22:31:30
【问题描述】:

我正在尝试使用 WPF 工具包 PropertyGrid 在 ReactiveUI 中的 ViewModel 上显示属性。由于 Reasons™,这些属性之一是自定义 Size 结构:

尺寸.cs

public struct Size
{
    public float Width { get; set; }
    public float Height { get; set; }
}

我的 ViewModel 公开的内容:

ElementViewModel.cs

public class ElementViewModel : ReactiveObject
{
    private Size _size;
    public Size Size
    {
        get => _size;
        set => this.RaiseAndSetIfChanged(ref _size, value);
    }
}

我正在使用自定义控件作为编辑器,它只使用两个 DoubleUpDowns 作为宽度和高度并手动更新绑定属性:

ElementSizeEditor.xaml

<UserControl x:Class="MyProject.Controls.ElementSizeEditor"
         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">
<Grid>
    <StackPanel Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Left" Orientation="Horizontal">
        <xctk:DoubleUpDown Width="75" TextAlignment="Right" VerticalContentAlignment="Center" 
                           Name="WidthEntry" AllowTextInput="True"
                           ShowButtonSpinner="False" FormatString="0.###" />
        <TextBlock Text="&quot; x " />
        <xctk:DoubleUpDown Width="75" TextAlignment="Right" VerticalContentAlignment="Center" 
                           Name="HeightEntry" AllowTextInput="True" 
                           ShowButtonSpinner="False" FormatString="0.###" />
        <TextBlock Text="&quot;" />
    </StackPanel>
</Grid>
</UserControl>

ElementSizeEditor.xaml.cs

public partial class ElementSizeEditor : UserControl
{
    public static readonly DependencyProperty ValueProperty = 
        DependencyProperty.Register("Value", 
            typeof(Size), 
            typeof(ElementSizeEditor),
            new PropertyMetadata(ValueChanged));

    // manually set the values on the width and height editors when we have a new Size
    private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        Size size = (Size)args.NewValue;
        ElementSizeEditor editor = (ElementSizeEditor)d;
        editor.WidthEntry.Value = size.Width;
        editor.HeightEntry.Value = size.Height;
    }

    public Size Value
    {
        get { return (Size)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    public ElementSizeEditor()
    {
        InitializeComponent();

        WidthEntry.ValueChanged += ValueChangedHandler;
        HeightEntry.ValueChanged += ValueChangedHandler;
    }

    // manually listen to value changes because we provide a new Size instance for new values
    private void ValueChangedHandler(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        if (WidthEntry.Value.HasValue && HeightEntry.Value.HasValue &&
            (!Core.Util.Math.AreEquivalent((float)WidthEntry.Value, Value.Width) ||
             !Core.Util.Math.AreEquivalent((float)HeightEntry.Value, Value.Height)))
            Value = new Size((float)WidthEntry.Value.Value, (float)HeightEntry.Value.Value);
    }
}

我将 ElementSizeEditor 注册为应用范围的静态资源:

App.xaml

<Application.Resources>
    <DataTemplate x:Key="ElementSizeEditor">
        <controls:ElementSizeEditor Value="{Binding Path=Value}"/>
    </DataTemplate>
</Application.Resources>

并在我的属性视图中使用它:

ElementPropertiesView.xaml

<UserControl x:Class="MyProject.Views.ElementPropertiesView"
         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">
<Grid>
    <xctk:PropertyGrid SelectedObject="{Binding}" SelectedObjectName="" SelectedObjectTypeName="{Binding Path=Name}" Grid.Row="0" AutoGenerateProperties="False"
                       ShowAdvancedOptions="False" ShowSortOptions="False" ShowSearchBox="False">
        <xctk:PropertyGrid.EditorDefinitions>
            <xctk:EditorTemplateDefinition TargetProperties="Size" EditingTemplate="{StaticResource ElementSizeEditor}" />
        </xctk:PropertyGrid.EditorDefinitions>
        <xctk:PropertyGrid.PropertyDefinitions>
            <xctk:PropertyDefinition TargetProperties="Size" DisplayName="Size" Description="The Element's size" />
        </xctk:PropertyGrid.PropertyDefinitions>
    </xctk:PropertyGrid>
</Grid>
</UserControl>

在代码隐藏中适当地绑定:

ElementPropertiesView.xaml.cs

public partial class ElementPropertiesView : UserControl, IViewFor<ElementViewModel>
{
    public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel",
        typeof(ElementViewModel),
        typeof(ElementPropertiesView));

    public ElementPropertiesView()
    {
        InitializeComponent();
        this.WhenActivated(d =>
        {
            d(this.WhenAnyValue(_ => _.ViewModel).BindTo(this, _ => _.DataContext));
        });
    }

    object IViewFor.ViewModel
    {
        get { return ViewModel; }
        set { ViewModel = (ColorElementViewModel)value; }
    }

    public ElementViewModel ViewModel
    {
        get { return (ColorElementViewModel) GetValue(ViewModelProperty); }
        set
        {
            SetValue(ViewModelProperty, value);
        }
    }
}

当我加载属性视图时,它将 ElementViewModel 正确绑定到 PropertyGrid 的 SelectedObject,正确显示 Size 属性的 ElementSizeEditor,正确绑定到 ElementSizeEditor 中的值,并在用户输入新时正确更新 ValueProperty价值观。

它没有将新的 Size 值反馈回 ElementViewModel,我不知道为什么。在其他情况下,我通过编写一个帮助程序类来解决这个问题,该类被公开并且知道如何进入 ElementViewModel 以更新关联的属性,但是这些实例更有意义,因为需要额外的功能。这次我只想用新的 Size 更新 ViewModel。

【问题讨论】:

    标签: wpf mvvm data-binding wpftoolkit reactiveui


    【解决方案1】:

    这很可能是因为您应该将 DataTemplate 上的绑定模式设置为 TwoWay。也就是说,在 App.xaml 中,应该将 改为

    【讨论】:

      猜你喜欢
      • 2014-04-16
      • 2011-09-19
      • 1970-01-01
      • 2015-12-08
      • 2011-02-01
      • 2017-12-04
      • 1970-01-01
      • 1970-01-01
      • 2013-06-18
      相关资源
      最近更新 更多