如何将元数据与WPF中的许多不同类型的控件相关联?

时间:2019-06-30 20:17:50

标签: wpf xaml user-interface reactiveui

我有一堆控件可能会基于其他控件中的错误而启用或未启用。当控件被禁用时,我想向用户说明为什么当前被禁用。但是,我已经在使用ToolTip来获取帮助文本。因此,我想当用户将鼠标悬停在变灰的组件上时,我会在静态文本框中显示错误消息。

xaml简化版

<StackPanel Margin="10 10 10 10">
    <TextBox x:Name="thtkDirTextBox" ToolTip="yadda yadda help text"/>
    <TextBox x:Name="tououDatTextBox" ToolTip="yadda yadda help text"/>
    <ComboBox x:Name="touhouExeSelector" ToolTip="yadda yadda help text"/>
    <ComboBox x:Name="stageSelector" ToolTip="yadda yadda help text"/>
    <ComboBox x:Name="sceneSelector" ToolTip="yadda yadda help text"/>
    <GroupBox Header="Errors">
        <TextBox IsEnabled="False" TextWrapping="Wrap" Width="200" FontSize="10" Height="50">
            Mouse over an item that's grayed out and the reason why will appear here.
        </TextBox>
    </GroupBox>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
        <Button Content="Save as..." ToolTip="yadda yadda help text"></Button>
        <Button Content="Quick Play" ToolTip="yadda yadda help text"></Button>
    </StackPanel>
</StackPanel>

至关重要的是,为了使其正常工作,每个组件都需要与自定义的元数据相关联以保存其错误消息。 (不同的控件可能有不同的变灰原因!)

我阅读了有关DependencyProperties的信息,但似乎使用它们需要一个控件的子类。如果必须创建TextBoxComboBoxButton的子类,那么我将得到在三个不同的不兼容DependencyProperty中重复的代码。 (而且,实际上它们的所有示例都涉及到使用触发器,我认为这对我没有帮助吗?)

我看到还有一个Tag属性。这是财产的合理使用吗?如果以后发现我需要另一个Tag属性怎么办?我是否应该假设这可能不会发生? (或者也许最终会实现这个目标,例如将JSON对象序列化为Tag以存储多个属性并不是没有道理的吗?)

我在这里应该做什么?


其他详细信息:

我正在使用ReactiveUI将信息从一个控件传播到另一个控件。对于每个可以显示为灰色的控件,ViewModel最终构造一个IObservable,其项要么是控件所需的某个值(例如,一个IEnumerable<string>可以选择ComboBox,要么可以是一个{{1 }}作为按钮有效性的证明)或错误消息。

鉴于这些可观察变量之一,我可以轻松将Unit类型的OneWayBind ObservableAsPropertyHelper应用于控件的bool属性,并希望对错误消息做类似的事情。如果要为此使用工具提示,那将非常简单:

IsEnabled

我还没有决定如何关联鼠标悬停事件。 (还没走那么远)

1 个答案:

答案 0 :(得分:1)

  

我看到还有一个Tag属性。这是对财产的合理使用吗?

更好的方法是创建一个可以在任何元素上设置的强类型attached dependency property。只需定义一个静态类即可在其中注册属性:

public static class Metadata
{
    public static readonly DependencyProperty InfoProperty = DependencyProperty.RegisterAttached(
          "Info",
          typeof(string),
          typeof(Metadata),
          new FrameworkPropertyMetadata(null));

    public static void SetInfo(UIElement element, string value) => element.SetValue(InfoProperty, value);

    public static string GetInfo(UIElement element) => (string)element.GetValue(InfoProperty);
}

您可以像这样在任何UIElement上进行设置:

<TextBox IsEnabled="False" TextWrapping="Wrap" Width="200" FontSize="10" Height="50"
             local:Metadata.Info="Mouse over an item that's grayed out and the reason why will appear here.">
</TextBox>

您可以使用GetInfo方法以编程方式获取值:

string info = Metadata.GetInfo(theControl);