Как я могу привязать поля класса к элементам внутри пользовательских шаблонов? (WPF)

#c# #wpf #binding

#c# #wpf #привязка

Вопрос:

Как я могу привязать поля класса к элементам внутри пользовательского шаблона ListBoxItem?? В этом примере кода отсутствуют некоторые аспекты, такие как определение класса, имя ListBox и т.д., Я просто испытываю трудности с процессом привязки.

 <ListBox>
    <ListBox.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>

                       <!-- how to bind class fields to these elements -->

                        <StackPanel Orientation="Horizontal">
                            <Label></Label>
                            <Image></Image>
                        </StackPanel>

                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>

    <!-- adding items dynamically -->

</ListBox>
  

Код C # должен выглядеть следующим образом:

 ListBoxItem listBoxItem = new ListBoxItem();
listBoxItem.Content = new MyClass(){ Name="MyName", Image="ImagePath"}
... append to ListBox ... 
  

Ответ №1:

При разработке в WPF поощряется MvvM для содействия разделению проблем. Для этого нужно было бы реализовать модель представления со свойствами, которые затем были бы привязаны к представлению. Когда вы хотите, чтобы пользовательский интерфейс (view) был в курсе изменений в данных, которые предоставляет View Model, нужно реализовать INotifyPropertyChanged интерфейс, например:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;    

namespace ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public BaseViewModel()
        {
             //ctor
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            UIThread( () =>
            {
                //make sure the event is raised on the main thread
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            });
        }

        Dispatcher _dispacther;

        protected void UIThread(Action action)
        {
            if (_dispacther == null)
            {
                _dispacther = Dispatcher.CurrentDispatcher;
            }
            _dispacther.Invoke(action);
        }
    }
}  
  

Ваш Hello класс был бы производным от BaseViewModel и предоставлял бы OnPropertyChanged(); в свойстве, подобном этому:

     private string name;

    public string Name
    {
        get { return name; }
        set { name= value; OnPropertyChanged(); }
    }
  

Теперь, когда вы меняете имя выбранного элемента в списке, это будет отражено в пользовательском интерфейсе. Попробуйте!
Эта реализация гарантирует, что событие вызывается в основном потоке, поэтому вам не нужно делать это при вызове события. Также [CallerMemberName] будет заполнено имя свойства, которое вы вызываете!

Ответ №2:

Разобрался. Класс C #:

         public class Hello
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string ImagePath { get; set; }
    }
  

Динамическое добавление C #:

 ListBoxItem lbi = new ListBoxItem();
lbi.Content = new Hello() { Name = "hello man", Description="I am superman.", ImagePath="Images/myimage.png"};
menu.Items.Add(lbi);
  

XAML:

             <ListBox Name="menu" ItemsSource="{Binding}">

                <ListBox.Resources>

                    <Style TargetType="ListBoxItem">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>

                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Label FontWeight="Bold" Content="{Binding Name}"></Label>
                                        <Label Content="{Binding Description}"></Label>
                                        <Image Source="{Binding ImagePath}" Width="30" Height="30"></Image>
                                    </StackPanel>

                                </DataTemplate>

                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.Resources>


            </ListBox>
  

Комментарии:

1. Помните о том, что INotfyPropertyChanged интерфейс ДОЛЖЕН БЫТЬ РЕАЛИЗОВАН, иначе ваши изменения не будут отражены на экране! Для привязки поддерживаются только свойства.

2. Объявите изменения, на которые вы ссылаетесь.