Привязать объект к комбинированному списку и текстовому полю - PullRequest
0 голосов
/ 18 июня 2019

У меня есть следующая модель:

public class Tag : ObservableObject
{
    private int _id = -1;
    public int Id
    {
        get { return _id; }
        set
        {
            if (value != _id)
            {
                _id = value;
                OnPropertyChanged("Id");
            }
        }
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (value != _name)
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    private string _freeText;
    public string FreeText
    {
        get { return _freeText; }
        set
        {
            if (value != _freeText)
            {
                _freeText = value;
                OnPropertyChanged("FreeText");
            }
        }
    }
}


public class Item : ObservableObject
{
    // ...

    private Tag _tag;
    public Tag Tag
    {
        get { return _tag; }
        set
        {
            if (value != _tag)
            {
                _tag= value;
                OnPropertyChanged("Tag");
            }
        }
    }
}

Следующая модель взгляда:

public class AppViewModel : ViewModelBase
{
    private Item _item;
    public Item Item
    {
        get { return _item; }
        set
        {
            if (_item != value)
            {
                _item = value;
                OnPropertyChanged("Item");
            }
        }
    }

    private List<Tag> _tags;
    public List<Tag> Tags
    {
        get { return _tags; }
        set
        {
            if (_tags != value)
            {
                _tags = value;
                OnPropertyChanged("Tags");
            }
        }
    }

    //...
 }

Где мой список тегов заполняется следующими данными:

Tags = new List<Tag>() 
{
   new Tag()
   {
     Id = 0,
     Name = "Free Text"
   },
   new Tag()
   {
     Id = 1,
     Name = "Tag 1"
   },
   new Tag()
   {
     Id = 2,
     Name = "Tag 2"
   },
   new Tag()
   {
     Id = 3,
     Name = "Tag 3"
   }
 }

и следующий вид:

<ComboBox x:Name="cmbTags"
          ItemsSource="{Binding Tags}"
          DisplayMemberPath="Name"
          SelectedItem="{Binding Item.Tag}"/>

<TextBox x:Name="txtTagFreeText" Text="{Binding ElementName=cmbTags, Path=SelectedItem.FreeText}">
    <TextBox.Style>
        <Style TargetType="TextBox">
            <Setter Property="Visibility" Value="Collapsed" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=cmbTags, Path=SelectedItem.Id}" Value="0">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

То, что я хочу сделать, это привязать источник элементов ComboBox к моему списку тегов, а элемент SelectedItem - к моему элементу. Мне удалось это настроить.

Затем, если выбран тег с идентификатором 0, я хочу показать TextBox, который также настроен.

У меня возникли проблемы с привязкой текста TextBox к свойству FreeText моего Item.Tag.

Привязка работает, но она применяется ко всем моим Предметам, поэтому всякий раз, когда я меняю свой Предмет, TextBox всегда будет отображать мой последний ввод.

Я также пытался изменить привязку TextBox Text к:

{Binding Item.Tag.FreeText}

Но у меня та же проблема.

Кто-нибудь знает, что я делаю не так? Нужно ли размещать больше кода?

Любая помощь или обратная связь приветствуется.

Спасибо.

1 Ответ

1 голос
/ 18 июня 2019

Вы привязываетесь к нулевому экземпляру. Свойство Item AppViewModel должно инициализироваться с экземпляром, но это не решит проблему, которую вы должны связать с ComboBox другим свойством в ViewModel, которое будет называться SelectedTag в установщике, обрабатывать логику, которую вы хотите.

public class AppViewModel : ObservableObject
{
    private Item _item = new Item();
    public Item Item
    {
        get { return _item; }
        set
        {
            if (_item != value)
            {
                _item = value;
                OnPropertyChanged("Item");
            }
        }
    }

    private Tag _SelectedTag;

    public Tag SelectedTag
    {
        get { return _SelectedTag; }
        set
        {
            if (_SelectedTag != value)
            {
                _SelectedTag = value;
                OnPropertyChanged("SelectedTag");
                Item.Tag = _SelectedTag;
                if (_SelectedTag.Id == 0)
                {
                    _SelectedTag.FreeText = "";
                }
            }
        }
    }


    private List<Tag> _tags;
    public List<Tag> Tags
    {
        get { return _tags; }
        set
        {
            if (_tags != value)
            {
                _tags = value;
                OnPropertyChanged("Tags");
            }
        }
    }

    // ...
}

и код XAML:

<ComboBox x:Name="cmbTags"
      ItemsSource="{Binding Tags}"
      DisplayMemberPath="Name"
      SelectedItem="{Binding SelectedTag,Mode=TwoWay}"/> 
    <TextBox  Text="{Binding SelectedTag.FreeText,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Setter Property="Visibility"  Value="Collapsed"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding SelectedTag.Id}" Value="0">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
</TextBox>

UPDATE

Другой способ связать напрямую, прослушивая Событие ItemChanged Item, когда Тег изменил Свойство Повышения Тега в AppViewModel и выполнил вашу логику.

public class AppViewModel : ObservableObject
{
    private Item _item;
    public Item Item
    {
        get { return _item; }
        set
        {
            if (_item != value)
            {
                _item = value;
                _item.PropertyChanged -= _item_PropertyChanged;
                _item.PropertyChanged += _item_PropertyChanged;
                OnPropertyChanged("Item");
            }
        }
    }

    private List<Tag> _tags;
    public List<Tag> Tags
    {
        get { return _tags; }
        set
        {
            if (_tags != value)
            {
                _tags = value;
                OnPropertyChanged("Tags");
            }
        }
    }

    public AppViewModel()
    {
        Item = new Item();
        Tags = new List<Tag>()
        {
           new Tag()
           {
             Id = 0,
             Name = "Free Text"
           },
           new Tag()
           {
             Id = 1,
             Name = "Tag 1"
           },
           new Tag()
           {
             Id = 2,
             Name = "Tag 2"
           },
           new Tag()
           {
             Id = 3,
             Name = "Tag 3"
           }
         };
    }

    private void _item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Item.Tag))
        {
            if (Item.Tag.Id == 0)
            {
                Item.Tag.FreeText = string.Empty;
            }
            OnPropertyChanged(nameof(Item));
        }
    }
}

И связать ComboBox с тегом товара напрямую

 <TextBox x:Name="txtTagFreeText" Text="{Binding Item.Tag.FreeText,Mode=TwoWay}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Setter Property="Visibility" Value="Collapsed" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Item.Tag.Id}" Value="0">
                        <Setter Property="Visibility" Value="Visible"/> 
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
</TextBox>
...