WPF / MVVM Загрузка UserControl во время выполнения - PullRequest
5 голосов
/ 30 декабря 2011

я знаю, что есть много статей о моей проблеме, но я не могу найти решение. Я новичок в WPF - MVVM, и я пытаюсь понять MVVM-Logic. Поэтому я сделал небольшой проект, чтобы понять это. Для моих более поздних приложений я хочу динамически загружать UserControls в мое окно.

В моем StartView у меня есть привязка к StartViewModel. (Привязка в APP.xaml)

StartView app = new StartView();
StartViewModel context = new StartViewModel();

StartView

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">
    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" />
    </Grid>
</Window>

StartViewModel

namespace test.ViewModel
{
    public class StartViewModel : ViewModelBase
    {
        #region Fields
        private UCStastistikViewModel _loadedControl;
        #endregion

        public StartViewModel()
        {
            LoadedControl = new UCStastistikViewModel();
        }

        #region Properties / Commands
        public UCStastistikViewModel LoadedControl
        {
            get { return _loadedControl; }
            set
            {
                if (value == _loadedControl)
                    return;

                _loadedControl = value;
                OnPropertyChanged("LoadedControl");
            }
        }
        #endregion

        #region Methods

        #endregion
    }
}

UCStatistikView

<UserControl x:Class="test.Views.UCStatistik"
             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:test.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="188" d:DesignWidth="508">
    <UserControl.DataContext>
        <vm:UCStastistikViewModel />
    </UserControl.DataContext>
    <Grid Background="red">       
    </Grid>
</UserControl>

UCStatistikViewModel

namespace test.ViewModel
{
    public class UCStastistikViewModel : ViewModelBase
    {
        #region Fields
        #endregion

        public UCStastistikViewModel()
        {
        }

        #region Properties / Commands
        #endregion

        #region Methods
        #endregion
    }
}

Теперь я хочу загрузить свой UCStatistikView в ContentControl моего StartView. Но в Startview вместо всего UC отображается только путь test.UCStatistikViewModel Кто-нибудь может дать мне несколько идей, где моя проблема / где я иду не так?

Пока J

Ответы [ 4 ]

18 голосов
/ 30 декабря 2011

Ваши ViewModels не должны заботиться о пользовательских элементах управления. Вместо этого пусть они удерживают ViewModel и позволяют WPF решить, как нарисовать ViewModel с помощью DataTemplate.

Например,

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.Views"
        xmlns:viewmodels="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">

    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodels:UCStastistikViewModel}">
            <views:UCStastistikView />
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" />
    </Grid>
</Window>

Кроме того, избавьтесь от <UserControl.DataContext> в вашем UserControl. DataContext должен передаваться тем, кто использует элемент управления, не определенный в UserControl:)

Редактировать

Исходя из вашего комментария к более раннему ответу о переключении содержимого StartPage путем переключения ViewModel, вам может быть интересно посмотреть мой пост . Он называется Navigation with MVVM, однако та же концепция применяется для переключения видов или пользовательских элементов управления

.

По сути, вы бы сделали свойство LoadedControl типа ViewModelBase вместо жесткого кодирования его типа, а затем задали бы его для любого объекта, который вы хотите отображать в вашем ContentControl. Шаблоны данных WPF позаботятся о подключении правильного представления для модели представления.

2 голосов
/ 30 декабря 2011

WPF не поддерживает автоматическое разрешение вида для данной модели вида. Наивным решением вашей проблемы будет непосредственное добавление UCStatistikView к вашему StartView и привязка к нему виртуальной машины

<Window x:Class="test.Views.StartView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:test.ViewModel"
    Title="Window1" Height="300" Width="516">
<Grid>
    <Menu IsMainMenu="True" Margin="0,0,404,239">
        <MenuItem Header="_Einstellungen">
            <MenuItem Header="Server" />
        </MenuItem>
    </Menu>
    <UCStatistikView DataContext="{Binding LoadedControl}" Margin="0,28,0,128" />
</Grid>

Более сложным подходом было бы реализовать ViewLocator (подход с представлением модели сначала) или ViewModelLocator (подход с представлением сначала). Локатор автоматически находит и связывает виды с моделью вида. Существует несколько инфраструктур / инструментариев MVVM, которые реализуют такой локатор.

  • Caliburn.Micro : предлагает гибкий ViewLocator и ViewModelLocator на основе соглашений об именах. Здесь есть статья о них
  • MVVM Light : Предлагает ViewModelLocator. Здесь представляет собой введение

С Caliburn.Micro ваш стартовый вид будет выглядеть так

<Window x:Class="test.Views.StartView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:views="clr-namespace:test.ViewModel"
    Title="Window1" Height="300" Width="516">
<Grid>
    <Menu IsMainMenu="True" Margin="0,0,404,239">
        <MenuItem Header="_Einstellungen">
            <MenuItem Header="Server" />
        </MenuItem>
    </Menu>
    <ContentControl cm:View.Model="{Binding LoadedControl}" Margin="0,28,0,128" />
</Grid>

Прикрепленное свойство cm:View.Model="{Binding LoadedControl}" сообщает caliburn, чтобы найти представление для связанной модели представления и установить для него свойство Content ContentControl.

1 голос
/ 30 декабря 2011

Вот что вы должны сделать:

StartView:

<Window x:Class="test.Views.StartView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:test.ViewModel"
        Title="Window1" Height="300" Width="516">
    <Grid>
        <Menu IsMainMenu="True" Margin="0,0,404,239">
            <MenuItem Header="_Einstellungen">
                <MenuItem Header="Server" />
            </MenuItem>
        </Menu>
        <UCStatistikView x:Name="myUCStatistikView" Margin="0,28,0,128" />
    </Grid>
</Window>

StartViewModel:

namespace test.ViewModel
{
    public class StartViewModel : ViewModelBase
    {
        private UCStastistikViewModel _myControlViewModel;

        public StartViewModel()
        {
            _myControlViewModel = new UCStastistikViewModel();
        }

        public UCStastistikViewModel MyControlViewModel
        {
            get { return _myControlViewModel; }
            set
            {
                if (value == _myControlViewModel)
                    return;

                _myControlViewModel = value;
                OnPropertyChanged("MyControlViewModel");
            }
        }
    }
}

UCStatistikView:

<UserControl x:Class="test.Views.UCStatistik"
             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:test.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="188" d:DesignWidth="508">
    <Grid Background="red">       
    </Grid>
</UserControl>

Код вашего StartView:

this.myUCStatistikView.DataContext = ((StartViewModel)this.DataContext).MyControlViewModel;

После тестирования различных подходов я пришел к выводу, что лучший способ для привязки текстовых данных - это выделение кода родительского представления в случае, если у вас есть userControls.

РЕДАКТИРОВАТЬ : локаторы ViewModel хороши для тривиальных примеров, но если ваш ViewModel должен быть создан динамически (это в основном имеет место, когда его конструктор требует параметров), вы не можете его использовать. Я лично перестал использовать локаторы из-за этого.

0 голосов
/ 26 декабря 2013

Обзор: - http://patelrocky1608.wordpress.com/2013/12/26/how-to-add-custom-control-dynamically-in-wpf/

, которые содержат весь код для динамического понимания UserControl ..

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