Установить DataContext для нескольких элементов управления с помощью XAML

#c# #wpf #mvvm #data-binding #viewmodel

#c# #wpf #mvvm #привязка данных #viewmodel

Вопрос:

У меня есть разные группы элементов управления, привязанные к разным категориям классов ViewModel.

ViewModels являются

  • MainViewModel
  • VideoViewModel
  • AudioViewModel

Вопрос

Как я могу установить DataContext с помощью XAML вместо C #?

1. Я попытался добавить DataContext="{Binding VideoViewModel}" в ComboBox XAML, но это не сработало, и элементы оказались пустыми.

2. Я также попытался сгруппировать все ComboBoxes определенной категории внутри UserControl с DataContext :

 <UserControl DataContext="{Binding VideoViewModel}">
    <!-- ComboBoxes in here -->
</UserControl>
  

3. Также попытался установить <Window> DataContext для себя DataContext="{Binding RelativeSource={RelativeSource Self}}"


Контекст данных

В настоящее время я устанавливаю DataContext этот способ для разных категорий элементов управления:

 public MainWindow()
{
    InitializeComponent();

    // Main
    this.DataContext =
    tbxInput.DataContext =
    tbxOutput.DataContext =
    cboPreset.DataContext =
    MainViewModel.vm;

    // Video
    cboVideo_Codec.DataContext =
    cboVideo_Quality.DataContext =
    tbxVideo_BitRate.DataContext =
    cboVideo_Scale.DataContext =
    VideoViewModel.vm;

    // Audio
    cboAudio_Codec.DataContext =
    cboAudio_Quality.DataContext =
    tbxAudio_BitRate.DataContext =
    tbxAudio_Volume.DataContext =
    AudioViewModel.vm;
}
  

XAML ComboBox

 <ComboBox x:Name="cboVideo_Quality" 
          DataContext="{Binding VideoViewModel}"
          ItemsSource="{Binding Video_Quality_Items}"
          SelectedItem="{Binding Video_Quality_SelectedItem}"
          IsEnabled="{Binding Video_Quality_IsEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          HorizontalAlignment="Left" 
          VerticalAlignment="Top" 
          Width="105" 
          Height="22"
          Margin="0,0,0,0"/>
  

Класс модели просмотра видео

 public class VideoViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void OnPropertyChanged(string prop)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(prop));
        }
    }


    public VideoViewModel() { }


    public static VideoViewModel _vm = new VideoViewModel();
    public static VideoViewModel vm
    {
        get { return _vm; }
        set
        {
            _vm = value;
        }
    }

    // Items Source
    private List<string> _Video_Quality_Items = new List<string>()
    {
        "High",
        "Medium",
        "Low",
    };
    public List<string> Video_Quality_Items
    {
        get { return _Video_Quality_Items; }
        set
        {
            _Video_Quality_Items = value;
            OnPropertyChanged("Video_Quality_Items");
        }
    }

    // Selected Item
    private string _Video_Quality_SelectedItem { get; set; }
    public string Video_Quality_SelectedItem
    {
        get { return _Video_Quality_SelectedItem; }
        set
        {
            if (_Video_Quality_SelectedItem == value)
            {
                return;
            }

            _Video_Quality_SelectedItem = value;
            OnPropertyChanged("Video_Quality_SelectedItem");
        }
    }

    // Enabled
    private bool _Video_Quality_IsEnabled;
    public bool Video_Quality_IsEnabled
    {
        get { return _Video_Quality_IsEnabled; }
        set
        {
            if (_Video_Quality_IsEnabled == value)
            {
                return;
            }

            _Video_Quality_IsEnabled = value;
            OnPropertyChanged("Video_Quality_IsEnabled");
        }
    }
}
  

Ответ №1:

Вы можете создать экземпляр объекта в xaml:

   <Window.DataContext>
      <local:MainWindowViewmodel/>
  </Window.DataContext>
  

И вы могли бы сделать это и для своих пользовательских моделей просмотра.

Более привычно создавать экземпляры любых дочерних viewmodels в window viewmodel. Отображается как общедоступные свойства, а datacontext дочерней viewmodel затем привязывается к этому свойству.

Я предлагаю вам сначала погуглить viewmodel и взглянуть на некоторые образцы.

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

1. Нужно было выполнить еще несколько шагов. Я использовал ваше предложение в XAML, чтобы связать их. Я опубликовал это как ответ, если вы хотите взглянуть.

Ответ №2:

Я не уверен, что это правильный способ, но я смог привязать группы ComboBoxes к разным ViewModels.


Я создал одну ViewModel, чтобы ссылаться на них все.

 public class VM: INotifyPropertyChanged
{
    ...

    public static MainViewModel MainView { get; set; } = new MainViewModel ();
    public static VideoViewModel VideoView { get; set; } = new VideoViewModel ();
    public static AudioViewModel AudioView { get; set; } = new AudioViewModel ();
}
  

Я использовал предложение Энди <local:VM> в MainWindow.xaml .

 <Window x:Class="MyProgram.MainWindow"
        ...
        xmlns:local="clr-namespace:MyProgram"
        >
    <Window.DataContext>
        <local:VM/>
    </Window.DataContext>
  

И использовал UserControl with DataContext , установленный в VideoView , с ComboBoxes внутри.

Вместо UserControl , также можно просто использовать VideoView.Your_Property_Name для каждой привязки.

 <UserControl DataContext="{Binding VideoView}">
    <StackPanel>
        <ComboBox x:Name="cboVideo_Quality" 
                  ItemsSource="{Binding Video_Quality_Items}"
                  SelectedItem="{Binding Video_Quality_SelectedItem}"
                  IsEnabled="{Binding Video_Quality_IsEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  HorizontalAlignment="Left" 
                  VerticalAlignment="Top" 
                  Width="105" 
                  Height="22"
                  Margin="0,0,0,0"/>

        <!-- Other ComboBoxes with DataContext VideoView in here -->

    </StackPanel>
</UserControl>
  

Затем, чтобы получить доступ к одному из свойств:

 VM.VideoView.Video_Codec_SelectedItem = "x264";
VM.VideoView.Video_Quality_SelectedItem = "High";

VM.AudioView.Audio_Codec_SelectedItem = "AAC";
VM.AudioView.Audio_Quality_SelectedItem = "320k";
  

Ответ №3:

Другие, очевидно, предоставили хорошие ответы, однако основной недостаток вашей привязки — это ваш первый набор DataContext = = = = = для модели основного представления.

Как только вы добавите контекст данных основной формы к модели ОСНОВНОГО представления, каждый элемент управления, находящийся там, ожидает СВОЕЙ НАЧАЛЬНОЙ точки в качестве модели ОСНОВНОГО представления. Поскольку ОСНОВНАЯ модель просмотра не имеет общедоступного свойства для моделей просмотра видео и аудио, она не может найти их для привязки do.

Если вы удалите «это.DataContext =», тогда не было бы контекста данных по умолчанию, и каждый элемент управления должен быть привязан так, как вы их предполагали.

Так что измените

 this.DataContext =
tbxInput.DataContext =
tbxOutput.DataContext =
cboPreset.DataContext =
MainViewModel.vm;
  

Для

 tbxInput.DataContext =
tbxOutput.DataContext =
cboPreset.DataContext =
MainViewModel.vm;
  

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

1. На самом деле это работало с this.DataContext и без него, причина, по которой я использовал это, заключалась в привязке строки заголовка программы. Но я хотел отказаться от его установки с помощью C # в MainWindow() . Я опубликовал ответ с новым способом, который я использую, я установил DataContext в xaml и get; set все разные ViewModels в одной ViewModel, которую я назвал VM .