Поле со списком WPF со связанным выбранным значением и статическими элементами, не распознающими выбор при инициализации - PullRequest
0 голосов
/ 12 сентября 2018

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

XAML

<Window x:Class="StaticComboBox.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:StaticComboBox"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:StaticUIVm}"
        Title="MainWindow"
        Height="450"
        Width="800">
   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition />
         <RowDefinition Height="Auto"/>
         <RowDefinition />
      </Grid.RowDefinitions>
      <ComboBox Grid.Row="1"
         SelectedValuePath="Tag"
                SelectedValue="{Binding SelectedValue, Mode=TwoWay}">
         <ComboBox.Items>
            <ComboBoxItem Content="Custom Display Text 111"
                          Tag="{Binding FirstValue}" />
            <ComboBoxItem Content="{Binding SecondValue.Item2}"
                          Tag="{Binding SecondValue}" />
         </ComboBox.Items>
      </ComboBox>
   </Grid>
</Window>

XAML.cs

using System.Windows;

namespace StaticComboBox
{
   /// <summary>
   /// Interaction logic for MainWindow.xaml
   /// </summary>
   public partial class MainWindow : Window
   {
      public MainWindow()
      {
         InitializeComponent();
         DataContext = new StaticUIVm();
      }
   }
}

StaticUIVm.cs

using StaticComboBox.Annotations;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace StaticComboBox
{
   public class StaticUIVm : INotifyPropertyChanged
   {
      public Tuple<long, string> FirstValue { get; set; }

      public Tuple<long, string> SecondValue { get; set; }

      private Tuple<long, string> _selectedValue;

      public Tuple<long, string> SelectedValue
      {
         get { return _selectedValue; }
         set
         {
            _selectedValue = value;
            OnPropertyChanged();
         }
      }

      public StaticUIVm()
      {
         FirstValue = new Tuple<long, string>(1, "Some Static Value");
         SecondValue = new Tuple<long, string>(2, "Some Other Static Value");

         SelectedValue = FirstValue;
      }

      public event PropertyChangedEventHandler PropertyChanged;

      [NotifyPropertyChangedInvocator]
      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

Моя проблема заключается в том, что, несмотря на то, что привязки работают правильно для элементов и отображения и когда я, как пользователь, выбираю значение, комбинированный список не отображается 't отражает правильный выбор при инициализации класса VM.Смысл, он не выбирает FirstValue.Это не имеет смысла для меня, поскольку ссылка должна быть точно такой же, и я подтвердил, что значение на виртуальной машине фактически изменяется во время инициализации.Я определенно инициализировал значения в конструкторе, и они уважались и отображались при загрузке, поэтому я немного запутался в том, где я ошибаюсь.

РЕДАКТИРОВАТЬ

Я принял ответ mm8, но мне пришлось внести несколько дополнительных изменений в XAML, чтобы заставить его вести себя по мере необходимости.Мне нужно было иметь возможность запускать пользовательский текст на основе значения идентификатора элементов, который был установлен во время выполнения.Из-за этого простой DataTrigger не будет работать, поэтому мне пришлось использовать MultiBinding.MultiBinding прервал отображение, когда был выбран элемент (как описано в ComboBox.ItemTemplate, не отображающий выделение должным образом ), поэтому мне пришлось установить IsEditable в false.Полный комбинированный список приведен ниже.

  <ComboBox Grid.Row="2"
            Grid.Column="1"
            IsEditable="False"
            ItemsSource="{Binding ItemSource}"
            SelectedItem="{Binding SelectedValue}">
     <ComboBox.ItemTemplate>
        <DataTemplate>
           <TextBlock>
              <TextBlock.Style>
                 <Style TargetType="TextBlock">
                    <Setter Property="Text"
                            Value="{Binding Name}" />
                    <Style.Triggers>
                       <DataTrigger Value="True">
                          <DataTrigger.Binding>
                             <MultiBinding Converter="{StaticResource LongEqualToLongMultiBindingDisplayConverter}">
                                <Binding Path="Id" />
                                <Binding Path="DataContext.FirstValue.Id" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}" />
                             </MultiBinding>
                          </DataTrigger.Binding>
                          <Setter Property="Text"
                                  Value="Custom Display Text 111" />
                       </DataTrigger>
                    </Style.Triggers>
                 </Style>
              </TextBlock.Style>
           </TextBlock>
        </DataTemplate>
     </ComboBox.ItemTemplate>
  </ComboBox>

Этот XAML в сочетании с предложениями из ответа mm8 (настройка коллекции, которая инициализируется во время выполнения из двух предоставленных значений) сделали свое дело.

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Почему бы вам просто не показать коллекцию выбираемых элементов из вашей модели представления? Вот как это решить, используя MVVM:

public class StaticUIVm : INotifyPropertyChanged
{
    public Tuple<long, string> FirstValue { get; set; }
    public Tuple<long, string> SecondValue { get; set; }

    private Tuple<long, string> _selectedValue;
    public Tuple<long, string> SelectedValue
    {
        get { return _selectedValue; }
        set
        {
            _selectedValue = value;
            OnPropertyChanged();
        }
    }

    public IEnumerable<Tuple<long, string>> Values { get; }

    public StaticUIVm()
    {
        FirstValue = new Tuple<long, string>(1, "Some Static Value");
        SecondValue = new Tuple<long, string>(2, "Some Other Static Value");
        Values = new Tuple<long, string>[2] { FirstValue, SecondValue };
        SelectedValue = SecondValue;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML:

<ComboBox x:Name="cmb" Grid.Row="1" ItemsSource="{Binding Values}"
                  SelectedItem="{Binding SelectedValue}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <TextBlock.Style>
                    <Style TargetType="TextBlock">
                        <Setter Property="Text" Value="{Binding Item2}" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Item1}" Value="1">
                                <Setter Property="Text" Value="Custom Display Text 111" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Вы можете даже удалить свойства FirstValue и SecondValue. Пользовательский текст определяется в представлении, но фактические параметры для выбора определяются в модели представления.

0 голосов
/ 12 сентября 2018

ОСНОВНЫЕ РЕДАКТЫ:

Поэтому, когда я впервые добавил ответ, я пытался использовать то, что у вас уже было, вместо того, чтобы демонстрировать, как я это сделаю. WPF является гибким и ограниченным в определенных отношениях, и я часто сталкиваюсь с проблемами. Ваш вопрос на самом деле довольно прост, когда вы используете другой подход. Многие из моих программ имеют элементы управления ComboBox, и, хотя я обычно заполняю коллекцию, аналогичный принцип можно применить с помощью вспомогательного класса данных над Tuple. Это значительно увеличит гибкость и будет более надежным.

Я добавил свойство SelectedIndex и связал его с int в вашем классе datacontext. Я также изменил SelectedValue на SelectedItem, так как в этом случае он намного превосходит его.

    <ComboBox Grid.Row="1"
              SelectedValuePath="Tag"
              SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
              SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}">

        <ComboBox.Items>
            <ComboBoxItem Content="{Binding FirstValue.display}"
                          Tag="{Binding FirstValue}" />
            <ComboBoxItem Content="{Binding SecondValue.display}"
                          Tag="{Binding SecondValue}" />
        </ComboBox.Items>
    </ComboBox>

Класс Datacontext, класс данных и метод расширения:

Итак, я переместил событие изменения вашей собственности в отдельный класс. Я рекомендую делать это, так как это делает его многоразовым. Это особенно удобно для класса данных. Теперь в конструкторе мы устанавливаем выбранный элемент И выбранный индекс.

public class StaticUIVm : PropertyChangeHelper
{
    private ComboBoxDataType _FirstValue;
    public ComboBoxDataType FirstValue
    {
        get { return _FirstValue; }
        set
        {
            _FirstValue = value;
            OnPropertyChanged();
        }
    }

    private ComboBoxDataType _SecondValue { get; set; }
    public ComboBoxDataType SecondValue
    {
        get { return _SecondValue; }
        set
        {
            _SecondValue = value;
            OnPropertyChanged();
        }
    }

    private ComboBoxDataType _SelectedItem;
    public ComboBoxDataType SelectedItem
    {
        get { return _SelectedItem; }
        set
        {
            _SelectedItem = value;
            OnPropertyChanged();
        }
    }

    private int _SelectedIndex;
    public int SelectedIndex
    {
        get { return _SelectedIndex; }
        set
        {
            _SelectedIndex = value;
            OnPropertyChanged();
        }
    }

    public StaticUIVm(string dynamicName)
    {
        FirstValue = new ComboBoxDataType() { id = 1, data = "Some Static Value", display = "Custom Display Text 111", };
        SecondValue = new ComboBoxDataType() { id = 2, data = dynamicName, display = dynamicName, };

        SelectedItem = FirstValue;
        SelectedIndex = 0;
    }
}

public class ComboBoxDataType : PropertyChangeHelper
{
    private long _id { get; set; }
    public long id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged();
        }
    }

    private string _data { get; set; }
    public string data
    {
        get { return _data; }
        set
        {
            _data = value;
            OnPropertyChanged();
        }
    }

    private string _display { get; set; }
    public string display
    {
        get { return _display; }
        set
        {
            _display = value;
            OnPropertyChanged();
        }
    }
}

public class PropertyChangeHelper : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...