0

Каким образом можно в главном окне подсовывать нужный UserControl без изменения параметра видимости. К примеру есть окно при запуске отображается FirstUserControl потом при каком нибудь событие (например нажатие на кнопку) FirstUcerControl отрабатывает (закрывается), и отображается новый SecondUserControl.

KJfe
  • 123
  • без изменения параметра видимости - т.е. смена изменением Visibility вас не устраивает? – Андрей NOP Jan 23 '18 at 08:11
  • @АндрейNOP нет такой вариант не устраивает, так как по сути SecondUserControl будет храниться в памяти и т.д. как только отработает FirstUserControl его параметры надо сразу же почистить ну или что то в этом вроде. – KJfe Jan 23 '18 at 08:15
  • 2
    Используйте ContentControl. В его свойстве Content вы можете выставлять нужный вам UserControl в любой момент времени. В MVVM вы просто создаете свойство типа object, к которому будете байндить свойство Content. – Иван Jan 23 '18 at 08:21
  • @John спасибо большое сейчас попробую:)) – KJfe Jan 23 '18 at 08:23

1 Answers1

2

Для этого используется DataTemplateSelector. Я покажу небольшой пример.

Допустим, у меня есть VM-классы CarVm и UserVm, которые я хочу отображать в одном окне, я набросал такую простую MainVm:

class MainVm : Vm
{
    object innerVm;
    public object InnerVm
    {
        get => innerVm;
        set => Set(ref innerVm, value, nameof(InnerVm));
    }

    public DelegateCommand ChangeCommand { get; }

    public MainVm()
    {
        ChangeCommand = new DelegateCommand(_ => InnerVm = (InnerVm is UserVm) ? (object)new CarVm() : (object)new UserVm());
    }
}

Тут всё просто, по команде у нас меняется свойство InnerVm, причем оно каждый раз будет менять тип.

Займемся разметкой, я в Grid добавлю ContentPresenter для отображения InnerVm и кнопку для вызова команды:

<Grid Margin="5">
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <ContentPresenter Content="{Binding InnerVm}">

    </ContentPresenter>

    <Button Grid.Row="1" Content="Change"
            HorizontalAlignment="Center"
            Command="{Binding ChangeCommand}"/>
</Grid>

Теперь нам для каждого отображаемого типа необходимо реализовать DataTemplate-разметку, я добавлю ее в ресурсы ContentPresenter:

        <ContentPresenter.Resources>
            <DataTemplate x:Key="UserTemplate">
                <StackPanel d:DataContext="{d:DesignInstance Type=c:UserVm}">
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="{Binding Age}"/>
                </StackPanel>
            </DataTemplate>

            <DataTemplate x:Key="CarTemplate">
                <StackPanel d:DataContext="{d:DesignInstance Type=c:CarVm}">
                    <TextBlock Text="{Binding Model}"/>
                    <TextBlock Text="{Binding MaxSpeed}"/>
                </StackPanel>
            </DataTemplate>
        </ContentPresenter.Resources>

Теперь пишем класс-наследник DataTemplateSelector:

class MyTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var fe = (FrameworkElement)container;
        if (item is UserVm) return fe.FindResource("UserTemplate") as DataTemplate;
        if (item is CarVm) return fe.FindResource("CarTemplate") as DataTemplate;
        return null;
    }
}

Создаем экземпляр этого класса в ресурсах Grid:

    <Grid.Resources>
        <c:MyTemplateSelector x:Key="MyTemplateSelector"/>
    </Grid.Resources>

И подключаем его к нашему ContentPresenter: ContentTemplateSelector="{StaticResource MyTemplateSelector}"

Готово!

шевелится

  • а чем этот подход лучше, чем просто выкинуть старый контрол и засунуть новый в ContentControl? – tym32167 Jan 23 '18 at 09:00
  • Не уверен, что понимаю зачем в данном случае использовать селектор - на мой взгляд проще установить DataType –  Jan 23 '18 at 09:00
  • @tym32167, ни чем не лучше, просто как один из вариантов – Андрей NOP Jan 23 '18 at 09:38
  • @FoggyFinder, хорошо знать несколько способов, в зависимости от ситуации можно использовать тот или иной подход – Андрей NOP Jan 23 '18 at 09:39
  • ну вот это и хочется понять, когда что лучше использовать – tym32167 Jan 23 '18 at 09:52
  • 2
    @tym32167, селектором удобно пользоваться, когда на один и тот же тип могут выдаваться разные шаблоны, например, в зависимости от каких-то других обстоятельств. Или наоборот, один и тот же шаблон на разные типы. В разметке такое не всегда возможно, да и громоздко обычно выходит. – Андрей NOP Jan 23 '18 at 10:04
  • @tym32167 В случае ContentPresenter по идее не будет нарушаться MVVM. Если просто менять UserControl в Content, то это значит, что VM будет напрямую управлять View, что несколько противоречит паттерну. В Presenter твоя VM будет управлять только другим VM, а вот View будет сама выбирать нужный View в зависимости от типа VM – Иван Jan 23 '18 at 11:03
  • 2
    @John MVVM может быть нарушен, если менять тем способом, что вы описали. Но это не единственный способ. Для таких вещей делают всякие контент-хелперы, регион-менеджеры, вью-адаптеры. Например, RegionManager – tym32167 Jan 23 '18 at 11:23
  • @АндрейNOP можно где-нибудь посмотреть полностью проект? Спасибо. – eusataf Oct 05 '23 at 13:06
  • @eusataf, к сожалению, ничего не осталось с тех пор – Андрей NOP Oct 05 '23 at 16:14