xaml - как показать отображаемое значение, отличное от значения его свойства, без использования KeyValuePair - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть ComboBox, который извлекает свои данные из списка строк.

Styles.Add("bold"); 
Styles.Add("italic");
Styles.Add("underline");
Styles.Add("");

Когда отображается в ComboBox, я хочу, чтобы он действительно отображал: {"жирный шрифт", "курсив", "подчеркивание""," [Discard style] "} соответственно.Важно, чтобы я мог изменить это значение пустой строки на display"[Discard style]", но также сохранить значение пустой строки при возникновении события SelectedChanged.

Есть ликакой-то способ сделать это (без изменения базовой структуры данных для поддержки KeyValuePair)?

Это то, с чего я изначально начал:

<ComboBox ItemsSource="{Binding Styles, Mode=OneWay}" SelectedItem="{Binding SelectedStyle}" SelectionChanged="StyleComboBox_SelectionChanged" />

С тех пор я пыталсядобавляя и делая множество вариантов / комбинаций

<ComboBox ItemsSource="{Binding Style, Mode=OneWay}" SelectedItem="{Binding SelectedStyle}" DisplayMemberPath="{Binding SelectedStyle, Converter={StaticResource StyleConverter}}" SelectionChanged="StyleComboBox_SelectionChanged" Height="22" Width="175" />

с SelectedValuePath={Binding SelectedStyle}, где StyleConverter выглядит следующим образом:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    string selectedStyle = value as string;

    if (string.IsNullOrEmpty(selectedStyle))
        return "[Discard style]";

    return value;
}

Я чаще всего сталкивался со сценарием, в котором отображаемое значениеявляется правильным, но базовое значение не является - оно как-то всегда всегда меняется на "[Discard style]".Может мне как-то нужны два конвертера?Один только для отображения в ComboBox, а другой только для выбора элемента?

Еще одно важное замечание: «[Стиль отмены]» будет переводиться в пользовательском интерфейсе в зависимости от операционной системы.Поэтому я не могу просто сравнить строку, чтобы увидеть, соответствует ли она, и превратить ее в пустую строку.

1 Ответ

0 голосов
/ 27 апреля 2019

На самом деле существует довольно много разных способов достижения этого, но для чего-то такого простого я бы, вероятно, просто использовал DataTemplate с DataTrigger, чтобы изменить текст, когда это пустая строка:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

<ComboBox ItemsSource="{Binding Styles, Mode=OneWay}" SelectedItem="{Binding SelectedStyle}">
    <ComboBox.Resources>
        <DataTemplate DataType="{x:Type sys:String}">
            <TextBlock>
                <TextBlock.Style>
                    <Style TargetType="{x:Type TextBlock}">
                        <Setter Property="Text" Value="{Binding}" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding}" Value="">
                                <Setter Property="Text" Value="[Discard style]" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

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

<Window.Resources>
    <conv:StringConverter x:Key="StringConverter" />
</Window.Resources>

<StackPanel Orientation="Vertical">
    <ComboBox ItemsSource="{Binding Styles, Mode=OneWay, Converter={StaticResource StringConverter}}" SelectedItem="{Binding SelectedStyle}" />
</StackPanel>

И тогда ваш конвертер будет выглядеть примерно так:

public class StringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value as IEnumerable<string>).Select(s => String.IsNullOrEmpty(s) ? "[Discard style]" : s);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

ОБНОВЛЕНИЕ: О, хорошо,Я вижу, что ты пытаешься сделать.В этом случае да, вам нужно использовать два преобразователя ... один для фильтрации списка строк, которые модель представления передает в представление, а затем второй для изменения значения, которое распространяется снова.

Прежде чем идти дальше, я должен отметить, что то, что вы пытаетесь сделать, - это действительно очень плохая идея.Весь смысл MVVM в том, что логика выполняется в модели представления.То, что вы действительно пытаетесь сделать, это реализовать логику на уровне представления, и это полностью противоречит всей парадигме MVVM / привязки данных.Вся цель модели представления состоит в том, чтобы подготовить данные в формате, который представление может легко использовать с минимальным количеством изменений;в тот момент, когда вы обнаруживаете, что вводите логику в представление (включая конвертеры, которые также являются частью этого слоя), это очень сильный признак того, что ваша модель представления не выполняет свою работу должным образом.

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

<ComboBox ItemsSource="{Binding Styles, Mode=OneWay, Converter={StaticResource StringConverter}}" SelectedItem="{Binding SelectedStyle, Converter={StaticResource SingleStringConverter}, Mode=OneWayToSource}" />

И второй конвертер:

public class SingleStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value?.ToString() == "[Discard style]") ? "" : value?.ToString();

    }
}

Если вам нужно сохранить программный выбор элементов, тогда единственный вариант - привязать SelectedIndex вместо SelectedItem, тогда ваша модель представления должна будет отвечать за поиск строки в списке источников.

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

...