首页 > 解决方案 > RibbonGallery.SelectedValue 不更新 SelectedItem

问题描述

我正在使用 Microsoft 功能区库:System.Windows.Controls.Ribbon. 我知道这个问题看起来很大,但我想要做的实际上并没有那么复杂,只涉及到几个部分。

目标

我正在尝试将 a 的选择绑定到RibbonComboBox我的一个类的属性,我将调用它TestBindingSource,但我需要能够取消选择的更改。因此,如果他们从 中选择一个项目RibbonComboBox,然后取消该更改,则选择需要保持原样。

显示的项目RibbonComboBox代表 an 的成员Enum,我将其称为TestEnum。我构建了另一个类TestEnumGalleryItem来表示 中的TestEnum值,RibbonComboBox并使用RibbonGallery.SelectedValueandRibbonGallery.SelectedValuePath绑定到 上的属性TestBindingSource。如果这太难理解,您应该能够从我的代码中看到我的意思。

编码

下面是我的真实代码的简化版,尽量不要为了风格给我扣太多分。我已经在一个新项目中对此进行了测试,它可以用来显示我遇到的问题。请记住添加对 Microsoft 功能区库的引用。

主窗口.xaml

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:VBTest"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Ribbon>
            <RibbonTab Header="Test">
                <RibbonGroup>
                    <RibbonComboBox Name="TestComboBox">
                        <RibbonGallery Name="TestGallery" MaxColumnCount="1" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectedValuePath="EnumValue" SelectedValue="{Binding BindingSource.TestEnumValue}">
                            <RibbonGallery.ItemsSource>
                                <x:Array Type="local:TestEnumGalleryCategory">
                                    <local:TestEnumGalleryCategory/>
                                </x:Array>
                            </RibbonGallery.ItemsSource>
                            <RibbonGallery.CategoryTemplate>
                                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                                    <HierarchicalDataTemplate.ItemTemplate>
                                        <DataTemplate>
                                            <RibbonGalleryItem ToolTipTitle="{Binding EnumName}" ToolTipDescription="{Binding EnumDescription}">
                                                <TextBlock Text="{Binding EnumName}" Margin="0, -3, -0, -3"/>
                                            </RibbonGalleryItem>
                                        </DataTemplate>
                                    </HierarchicalDataTemplate.ItemTemplate>
                                </HierarchicalDataTemplate>
                            </RibbonGallery.CategoryTemplate>
                        </RibbonGallery>
                    </RibbonComboBox>

                    <RibbonButton Label="Break" Click="RibbonButton_Click"/>
                </RibbonGroup>                
            </RibbonTab>
        </Ribbon>
    </Grid>
</Window>

主窗口.xaml.vb

Imports System.ComponentModel

Class MainWindow
    Public Sub New()
        BindingSource = New TestBindingSource
        InitializeComponent()
    End Sub

    Public Property BindingSource As TestBindingSource
        Get
            Return GetValue(BindingSourceProperty)
        End Get
        Set(ByVal value As TestBindingSource)
            SetValue(BindingSourceProperty, value)
        End Set
    End Property
    Public Shared ReadOnly BindingSourceProperty As DependencyProperty =
                           DependencyProperty.Register("BindingSource",
                           GetType(TestBindingSource), GetType(MainWindow))

    Private Sub RibbonButton_Click(sender As Object, e As RoutedEventArgs)
        Stop
    End Sub
End Class

Public Enum TestEnum
    ValueA
    ValueB
End Enum

Public Class TestEnumGalleryCategory
    Public Property Items As New List(Of TestEnumGalleryItem) From {New TestEnumGalleryItem With {.EnumValue = TestEnum.ValueA, .EnumName = "Value A", .EnumDescription = "A's description"},
                                                                    New TestEnumGalleryItem With {.EnumValue = TestEnum.ValueB, .EnumName = "Value B", .EnumDescription = "B's description"}}
End Class

Public Class TestEnumGalleryItem
    Public Property EnumValue As TestEnum = TestEnum.ValueA

    Public Property EnumName As String

    Public Property EnumDescription As String
End Class

Public Class TestBindingSource
    Implements INotifyPropertyChanged

    Private _TestEnumValue As TestEnum = TestEnum.ValueA
    Property TestEnumValue As TestEnum
        Get
            Return _TestEnumValue
        End Get
        Set(value As TestEnum)
            'Don't actually set new value, just leave it the same to simulate cancelation
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(TestEnumValue)))
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
End Class

问题

当您运行代码时,您会看到RibbonComboBox默认情况下显示“值 A”。将选择更改为“值 B”。更改的选择RibbonComboBox现在显示“值 B”。这不是我想要发生的,选择应该立即变回“值 A”。

如果您查看 的代码TestBindingSource.TestEnumValue,您会发现设置时我实际上并没有保留新值,而是保留旧值以模拟用户取消更改。然后我引发PropertyChanged事件以更新 UI,以便它知道属性的真正价值是什么。

更改为“Value B”后,单击“Break”按钮(为方便起见包括在内)。在 Visual Studio 监视窗口中,比较 和 的TestGallery.SelectedItemTestGallery.SelectedValue。你会看到它TestGallery.SelectedValue拥有正确的TestEnumValueA。现在看一下TestGallery.SelectedItem,您会发现它仍然包含表示 的项目ValueB

因此,即使RibbonGallery已正确告知该值现在应该是ValueA,它仍然显示ValueB。我该如何解决?

我会跟你讲道理,我没有太多时间花在这个 bug 上,而且我已经习惯了在涉及到功能区时不得不做出一些变通的解决方法。您可以为我提供有关如何 正确更新RibbonGallery(以及因此RibbonComboBox)正确更新的任何解决方案,我们将不胜感激。

标签: wpfvb.netdata-bindingribbon

解决方案


经过更多的测试和研究,我意识到这个问题并不是功能区库所独有的。实际上,这似乎也是正常的问题,ComboBox大概是所有的问题ItemsControls。一旦我意识到这一点,我就能够更有效地寻找答案并在这里找到解决方案:
https ://nathan.alner.net/2010/04/25/cancelling-selection-change-in-a-bound-wpf -组合框/

这不是一个完全干净的解决方案,但在我的具体情况下,将值设置为新选择然后立即将其设置回不会导致任何问题,所以我就是这样做的。作为记录,在重新设置选择时,我使用DispatcherPriority.DataBind而不是DispatcherPriority.ContextIdle,这样更改甚至不会显示在 UI 中,但解决方案仍然有效。


推荐阅读