интересная привязка ContentControl

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

<Window x:Class="ContentControl.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:ContentControl.ViewModels"
    xmlns:views="clr-namespace:ContentControl.Views"
    Title="MainWindow" Height="350" Width="525">  
<Window.Resources>
    <DataTemplate DataType="{x:Type vm:ScreenViewModel}">
        <views:ScreenView DataContext="{Binding}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:WelcomeViewModel}">
        <views:WelcomeView DataContext="{Binding}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:MeetingRoomViewModel}">
        <views:MeetingRoomView DataContext="{Binding}"/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type vm:DashboardViewModel}">
        <views:DashboardView />
    </DataTemplate>
</Window.Resources>

<Grid>
    <StackPanel>
        <Label>This Is My Label</Label>
        <ContentControl x:Name="MainPanel" Content="{Binding Path=Content}"
            MinHeight="200"
            MinWidth="200"
            HorizontalContentAlignment="Left" 
            VerticalContentAlignment="Center" 
            Focusable="False">
        </ContentControl>
    </StackPanel>
</Grid>
</Window>

КОД:

public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        MainPanel.Content = new WelcomeView();
        MainPanel.Content = this.MainPanel.Content;
    }
}

Вот модель WelcomeViewModel:

internal class WelcomeViewModel : BaseViewModel
{
    private MainWindowViewModel _mainWindowVm;
    private RelayCommand<string> _viewChangedCommand;

    public ICommand ViewChangedCommand
    {
        get { return _viewChangedCommand ?? (_viewChangedCommand = new RelayCommand<string>(OnViewChanged)); }
    }

    public event EventHandler ViewChanged;

    private void OnViewChanged(string view)
    {
        EventHandler handler = ViewChanged;
        if (handler != null) handler(view, EventArgs.Empty);
    }

    public MainWindowViewModel MainWindowVm
    {
        get { return _mainWindowVm; }
        set
        {
            _mainWindowVm = value;
            OnPropertyChanged("MainViewModel");
        }
    }

    public WelcomeViewModel()
    {
        MainWindowVm = new MainWindowViewModel();
        ViewChanged += MainWindowVm.ViewChanged;
    }
}

И, наконец, мой welcome.xaml

<UserControl x:Class="ContentControl.Views.WelcomeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:vm="clr-namespace:ContentControl.ViewModels"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
    <vm:WelcomeViewModel />
</UserControl.DataContext>
<Grid Background="red">
    <Grid.RowDefinitions >
        <RowDefinition Height="25*" />
        <RowDefinition Height="50*"/>
        <RowDefinition Height="25*"/>
    </Grid.RowDefinitions>

    <Rectangle Grid.Row="0" Fill="Green"/>
    <DockPanel Grid.Row="1" HorizontalAlignment="Center" Background="White">
        <Button Height="50" Width="50" Margin="5" Content="DASH" Command="{Binding ViewChangedCommand}" CommandParameter="Dashboard"/>
        <Button Height="50" Width="50" Margin="5" Content="ROOM" Command="{Binding ViewChangedCommand}" CommandParameter="MeetingRoom"/>
        <Button Height="50" Width="50" Margin="5" Content="SCREEN" Command="{Binding ViewChangedCommand}" CommandParameter="Screen" />
    </DockPanel>
    <Rectangle Grid.Row="2" Fill="Blue"/>
</Grid>
</UserControl>

Таким образом, проблема заключается в том, что когда событие ViewChange запускается, оно отображается в MainWindowViewModel, однако, когда оно использует PropertyEventHandler (как показано ниже), PropertyChanged всегда имеет значение null.

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }   
    }
}

person KitWat    schedule 21.09.2012    source источник


Ответы (2)


Ну, вот вам и ошибки WPF, Bindings и Mvvm... Во-первых, зачем вы это делаете:

MainPanel.Content = this.MainPanel.Content;

то же самое, что:

MainPanel.Content = MainPanel.Content;

эта строка необязательна.

Во-вторых, почему ты сказал:

Content="{Binding Path=Content}"

но затем вы устанавливаете свой код позади:

MainPanel.Content = new WelcomeView();

Здесь у вас может быть концептуальная ошибка: когда вы устанавливаете привязку по умолчанию, эта привязка будет выполняться для DataContext самого элемента управления (в данном случае UserControl). Хорошо, чтобы решить эту проблему и работать с Mvvm, давайте сохраним привязку:

Content="{Binding Path=Content}"

Но теперь нам нужно установить контекст данных UserControl:

MainPanel.DataContext = new MainPanelViewModel();

Теперь нам нужно создать свойство в MainPanelViewModel с именем Content. Внутри этого свойства вы зададите содержимое, которое хотите отобразить в ContentControl.Content. (В этом случае WelcomeViewModel и все, что вы хотите)

Надеюсь, этот ответ поможет вам начать работу с wpf и mvvm. это отличная платформа.

person Raúl Otaño    schedule 21.09.2012
comment
Основная панель — это имя элемента управления содержимым. Я хочу, чтобы MainPanel был контейнером для разных представлений. Следовательно, установите его на WelcomeView(); затем я хочу иметь возможность привязываться к различным представлениям MainPanel путем выбора кнопки, которая находится в первом представлении (welcomeView). - person KitWat; 21.09.2012
comment
Затем эти разные представления вы должны установить в свойстве Content вашей модели представления. Но тогда команда, которая меняет представления (ViewChangedCommand), также должна быть внутри ViewModel (пример MainPanelViewModel) - person Raúl Otaño; 21.09.2012
comment
Вы должны создать новый класс (в данном случае назовите его MainPanelViewModel), который будет ViewModel для MainPanel contentControl, затем вы должны установить этот класс как DataContext MainPanel ContentControl или MainWindow. Затем в этом классе установите свойство Content и ViewChangedCommand. Команда изменения представления задает свойство содержимого (содержимое, которое вы будете отображать на своей основной панели). - person Raúl Otaño; 21.09.2012
comment
Я хочу использовать для этого один класс, зачем мне два? Можете ли вы указать пример (код) - person KitWat; 21.09.2012
comment
Нужна ли вам модель представления для каждого логического представления, в этом случае MainPanelViewModel, о которой я сказал, может быть моделью представления для основного окна (также вы должны назвать ее MainWindowViewModel). А модель WelcomeViewModel должна работать как модель представления для представления приветствия. Идея состоит в том, что вы устанавливаете WelcomeViewModel в содержимом MainPanelViewModel и загружаете WelcomeView. (загрузить содержимое CONtentControl) - person Raúl Otaño; 21.09.2012
comment
Вы слышали о MvvMLigth, он отлично подходит для MvvmApplications - person Raúl Otaño; 21.09.2012
comment
Да, поэтому мой проект состоит из MainWindowView, MainWindowViewModel (это представление, в котором я хотел бы показать другие представления), другие файлы: WelcomeView, WelcomeViewModel и ScreenView, ScreenViewModel. Вся идея состоит в том, чтобы представление MainWindow отображалось с представлением приветствия внутри элемента управления содержимым (в MainWindowView), а затем, когда вы нажимаете кнопку на элементе управления приветствием, оно изменяет элемент управления содержимым MainWindowView, чтобы отображать в нем ScreenView. - person KitWat; 21.09.2012
comment
Хорошим примером является другой ваш вопрос (Wpf contentControl), у вас есть MainWindowViewModel, в этой ViewModel вы должны поставить Commands и свойство Content, а затем в командах изменить содержимое. - person Raúl Otaño; 21.09.2012
comment
Вот что я пытаюсь СДЕЛАТЬ! - person KitWat; 21.09.2012
comment
Спасибо, ребята, я понял это - person KitWat; 25.09.2012

Хорошо. Ошибки, которые вы могли бы исправить:

  • не изменяйте MainPanel.Content в коде программной части. Это должно быть изменено во ViewModel через привязку.
  • Обратите внимание, что в вашем Window.Resources вы устанавливаете для DataContext значение MainViewModel для WelcomeView, а в WelcomeView вы хотите, чтобы это было WelcomeIewModel. Это так не работает. DataCOntext=WelcomeViewModel переопределяется Window.Resources
  • почему вы создаете новую модель MainViewModel в WelcomeViewModel?
  • PropertyChanged имеет значение null, потому что вы не используете его в представлении (этом конкретном экземпляре MainiewMOdel). Если вы собираетесь выполнить привязку к нему, PropertyChanged получит новый прослушиватель событий и больше не будет нулевым.

Возможно, объясните свою проблему немного лучше, и я могу дать дополнительную информацию.

person Erti-Chris Eelmaa    schedule 21.09.2012
comment
Я изменил его в коде, чтобы инициализировать его с помощью welcomeView. Я создал MainViewModel в приветствии, чтобы я мог прослушивать событие ViewChanged. ' MainWindowVm = новая MainWindowViewModel(); ViewChanged += MainWindowVm.ViewChanged;' как мне получить этот конкретный экземпляр? - person KitWat; 21.09.2012