Как связать метаданные со многими различными типами элементов управления в WPF? - PullRequest
0 голосов
/ 30 июня 2019

У меня есть несколько элементов управления, которые могут включаться или не включаться в зависимости от ошибок других элементов управления. Когда элемент управления отключен, я хочу дать пользователю объяснение, почему он в настоящее время отключен. Тем не менее, я уже использую подсказку для текста справки. Поэтому я подумал, что сообщения об ошибках будут появляться в статическом текстовом поле, когда пользователь наведет курсор на серый компонент.

Упрощенный 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, но, похоже, что для их использования требуется подкласс элемента управления. Если мне нужно создать подклассы TextBox, ComboBox и Button, то я получу дублированный код для трех разных несовместимых свойств DependencyProperties. (кроме того, практически все их примеры включают использование триггеров, которые, я не думаю, могут мне здесь помочь?)

Я вижу, что есть также свойство Tag. Это добросовестное использование имущества? Что если позже я обнаружу, что мне нужно другое свойство Tag? Должен ли я просто предположить, что это, вероятно, не произойдет? (Или, может быть, если это в конечном итоге приведет к этому, возможно, не исключено, например, сериализовать объекты JSON в Tag для хранения нескольких атрибутов?)

Что мне здесь делать?


Дополнительные сведения:

Я использую ReactiveUI для распространения информации от одного элемента управления к другому. Для каждого элемента управления, который может быть выделен серым цветом, ViewModel в конечном итоге создает IObservable, элементы которого либо являются некоторым значением, необходимым для элемента управления (например, IEnumerable<string> с возможностью выбора для ComboBox, либо, возможно, просто Unit в качестве доказательства срок действия кнопки) или сообщение об ошибке.

Учитывая одну из этих наблюдаемых, я легко могу OneWayBind a ObservableAsPropertyHelper типа bool со свойством IsEnabled элемента управления и надеюсь сделать что-то подобное для сообщения об ошибке. Если бы я использовал для этого всплывающие подсказки, это было бы довольно просто:

// ViewModel
this.stageSelectorErrorMessage =
    this.stageSelectorEithers
    .Select(either => either.Error) // string on failure, null on success
    .ScheduleOn(RxApp.MainThreadScheduler)
    .ToProperty(this, x => x.StageSelectorErrorMessage);

// View
this.WhenActivated(disposableRegistration => {
    // ...
    this.OneWayBind(this.ViewModel,
        viewModel => viewModel.StageSelectorErrorMessage,
        view => view.stageSelector.ToolTip
    ).DisposeWith(disposableRegistration);
})

Я еще не решил, как подключить события наведения мыши. (не зашел так далеко)

1 Ответ

1 голос
/ 01 июля 2019

Я вижу, что есть также свойство Tag. Это справедливое использование имущества?

Лучшим подходом было бы создать строго типизированное свойство присоединенной зависимости , которое вы можете установить для любого элемента. Просто определите статический класс, в котором вы регистрируете свойство:

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);
...