Как переключаться между представлениями с помощью DataTemplate + Triggers

У меня есть требование, когда пользователь может переключаться для просмотра иерархических данных либо в виде дерева, либо в виде текста в сетке данных, либо в виде блок-схемы.

Пользователь может сделать это, нажав кнопку переключения с надписью «Переключить режим». Я хочу сделать все это таким образом, чтобы это можно было обрабатывать только в представлении, поскольку ViewModel во всех трех случаях одинаков.

Как применить View к моей ViewModel на основе Trigger.


person Pradeep    schedule 01.07.2011    source источник


Ответы (2)


Если состояние отображаемого представления сохраняется в некотором свойстве перечисления, вы можете использовать ссылку ContentControl и DataTriggers например:

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:TreeModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
                    <Setter Property="Content">
                        <Setter.Value>
                            <uc:GridModeView />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

(Поскольку стиль используется только в одном месте, установив его непосредственно как ContentControl.Style, это сработает, если вы хотите использовать его более чем в одном месте, вместо этого следует установить ContentTemplate, потому что в противном случае будет только один экземпляр представления, совместно используемый все элементы управления со стилем, который не разрешен WPF (конечно, Content необходимо установить на что-то для применения шаблона))

Вы также можете напрямую привязаться к IsChecked из ToggleButton, используя ElementName конечно. Тогда соответствующие значения будут True, False и {x:Null}.

person H.B.    schedule 01.07.2011
comment
Мне нравится этот ответ, но он действительно показывает, насколько многословным иногда может быть WPF. - person Chris Lees; 21.09.2015

Ответ HB хорош, но есть сценарии, в которых он не так хорош.

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

В некоторых сценариях имеет смысл создать оба представления в одном контейнере (например, Grid) и вместо этого переключать их Visibility. Если вы используете ленивую оценку в своих моделях представления, ресурсоемкие операции не будут выполняться до тех пор, пока представление не станет видимым, и, что важно, они будут выполняться только в первый раз, когда представление становится видимым. После отображения обоих представлений пользователь может переключаться между представлениями, не реконструируя лежащие в их основе модели представлений.

Изменить:

Я исправляюсь, вроде как: ответ Х.Б. не так хорош, как выглядел.

Вы не можете использовать стиль, чтобы установить для свойства Content объекта ContentControl значение UIElement. См. этот блог. post для получения полной информации, но вкратце это то, что если вы используете подход HB, вы получите ошибку времени выполнения.

Вместо этого вы можете установить свойство ContentTemplate, например:

<Style TargetType="{x:Type ContentControl}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="TreeMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:TreeModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding ViewMode}"
                        Value="GridMode">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <uc:GridModeView/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>
person Robert Rossney    schedule 01.07.2011
comment
На самом деле это не так, в отличие от шаблонов данных, сеттеры создают только один экземпляр этих объектов, вы можете проверить это, просто используя TextBox в качестве содержимого и изменив его текст, а затем прокручивая представления, он все еще будет там. Если бы это было не так, не было бы таких проблем, как эта. - person H.B.; 02.07.2011
comment
Это интересно. Пока я пытался создать демонстрацию, чтобы определить, верно ли это в данном случае, я обнаружил, что следствием того, что вы описываете, является то, что подход в вашем ответе на самом деле вообще не работает. Смотрите мою правку. - person Robert Rossney; 02.07.2011
comment
Возможно, вы неправильно поняли проблему, ключевое предложение: Это вызывает исключение, поскольку значение Setter.Value является общим, а объекты, которые наследуются от UIElement, не могут иметь более одного родителя. Итак, если этот стиль никогда не используется более чем для одного ContentControl (что можно обеспечить, установив его напрямую, а не предоставив его в качестве ресурса), это вообще не проблема. (Я также использовал этот метод в одном из приложений раньше, и он отлично работает). - person H.B.; 02.07.2011
comment
На самом деле вы даже можете заставить это работать для нескольких целей стиля, вам просто нужно определить стиль как ресурс с x:Shared установите значение false, попробуйте. (Например, насколько это круто?) - person H.B.; 02.07.2011
comment
Что ж, я проверил ваш код - я скопировал его в проект и создал элементы управления TreeViewMode и GridModeView, и, как указано в сообщении в блоге, на которое я ссылаюсь, он падает. Я сделал что-то неправильно? - person Robert Rossney; 03.07.2011
comment
Вполне возможно, но я не могу сказать вам, что без кода (или сообщений об исключениях, если уж на то пошло). Я только что проверил это снова, скопировав код в своем ответе, и он сработал так, как я ожидал. Если вы разместите свой код где-нибудь или загрузите весь проект, я могу взглянуть на него, если хотите. - person H.B.; 03.07.2011
comment
Эй, недавно я наткнулся на другую статью об этом, оказывается, вы не могли установить Content для элемента пользовательского интерфейса до .NET 4. Поэтому я предполагаю, что вы использовали 3.5, когда тестировали это. - person H.B.; 11.01.2012