Свернуть / Видимые пользовательские элементы управления при нажатии кнопки с помощью MVVM - без механизма подкачки -

StackOverflow https://stackoverflow.com/questions/2097253

  •  21-09-2019
  •  | 
  •  

Вопрос

В моем сценарии у меня есть MainView + MainViewModel, UserControl1 + UserControl 2.В MainView у меня есть 2 кнопки с надписью:Button_ShowUserControl1 + Button_ShowUserControl2.В нижней части MainView у меня есть "ContentGrid", который принимает /should_take ...каждый пользовательский элемент управления.

Моя цель:

При нажатии кнопки Button_ShowUserControl1 Пользовательский контроль1 является Видимый и Пользовательский контроль2 или любой другой пользовательский элемент управления должен быть установлен в Рухнул.То же самое справедливо для Button_ShowUserControl2.

Моя проблема:

1.) Поскольку пользовательские элементы управления должны быть загружены при запуске приложения, как я могу объединить их все в одну "ContentGrid"?На самом деле это невозможно...итак, как я могу сделать один пользовательский элемент управления видимым, в то время как другой находится в том же месте / "ContentGrid" только что рухнул?

2.) Поскольку 1.) кажется невозможным, как я могу создать экземпляр всех пользовательских элементов управления при запуске приложения и сделать их видимыми / свернутыми только при нажатии соответствующей кнопки?

3.) Поскольку UserControl имеет свойство Visibility = Visible / Hidden / Collapsed , как я могу привязаться к свойству в ViewModel, возвращающему такое значение, как Collapsed?Я мог бы получить только логическое значение типа Visibility = false / true?

Мой тестовый код:

<Grid x:Name="LayoutRoot" Background="#FFBDF5BD" ShowGridLines="False">
    <Grid.RowDefinitions>
        <RowDefinition Height="96*" />
        <RowDefinition Height="289*" />
    </Grid.RowDefinitions>      
    <Grid HorizontalAlignment="Stretch" Name="MenuGrid" VerticalAlignment="Stretch" Background="#FFCECEFF">
        <StackPanel Name="stackPanel1" Background="#FFEDFF00" Orientation="Horizontal">
            <Button Content="User Data 1" Height="35" Name="button1" Command="{Binding  Path=ShowUserControl1Command}" Width="150" Margin="100,0,0,0" />
            <Button Content="User Data 2" Height="35" Name="button2" Width="150" Margin="100,0,0,0" />
        </StackPanel>
    </Grid>
    <Grid Grid.Row="1" HorizontalAlignment="Stretch" Name="ContentGrid" VerticalAlignment="Stretch" Background="#FFB15454" />
</Grid>

<UserControl x:Class="SwapUserControls.MVVM.UserControl2"
         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:SwapUserControls.MVVM.ViewModel"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" Visibility="{Binding IsUserControl1Collapsed, Path=Value}">

<UserControl.Resources>
    <vm:MainViewModel x:Key="MainViewModelID" />
</UserControl.Resources>

<UserControl.DataContext>
    <Binding Source="{StaticResource MainViewModelID}" />
</UserControl.DataContext>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="228*" />
        <RowDefinition Height="72*" />
    </Grid.RowDefinitions>
    <Button Content="UserControl2" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="112,27,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
    <DataGrid HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Stretch" Background="#FFC046F8" />
</Grid>

public class MainViewModel : ViewModelBase
{
    RelayCommand _ShowUserControl1Command;
    private bool _IsUserControl1Collapsed;

    public RelayCommand ShowUserControl1Command
    {
        get
        {
            if (_ShowUserControl1Command == null)
            {
                _ShowUserControl1Command = new RelayCommand( () => ShowUserControl1() );                       
            }
            return _ShowUserControl1Command;
        }
    }

    public void ShowUserControl1()
    {
        _IsUserControl1Collapsed = true;
    }

    public bool IsUserControl1Collapsed 
    {          
        get
        {
            return _IsUserControl1Collapsed;
        }  
    }        
}

Да, код неправильный, поэтому я спрашиваю здесь :)

Это было полезно?

Решение

У вас есть только 2 ошибки в этом коде.

1) Вы не можете установить видимость пользовательского элемента управления напрямую...вы должны установить его на контейнер:

<Grid Visibility="Collapsed">
    <myControls:MyUserControl />
</Grid>

2) Видимость - это не логическое значение, это перечисление.Таким образом, вам нужно будет использовать конвертер для преобразования из логического значения в видимость.Наблюдать:

<Window ...>
<Window.Resources>
     <BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>

<Grid Visibility="{Binding ShouldShowUsercontrol1, Converter={StaticResource BoolToVis}}">
     <myControls:MyUserControl />
</Grid>
</Window>

Так и должно быть.Надеюсь, это поможет.

Есть и другие вещи, о которых вы оставляете подсказки, которые могут повлиять на способность этого работать.Например, вы не показываете самый большой элемент контейнера...вы оборачиваете все в StackPanel?Например, если вы оборачиваете все в сетку, элементы управления будут накладывать все слоями.

Попробуйте эти изменения, которые я предлагаю...это должно приблизить вас.


Редактировать:Еще одна идея с использованием шаблонов данных

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

public class MyFirstViewModel : ViewModel
{

}

public class MySecondViewModel : ViewModel
{

}

Затем из вашей "родительской" или "основной" ViewModel вы показываете или скрываете нужные вам представления, поскольку они есть в коллекции ViewModels:

public MyMainViewModel : ViewModel
{
     public ObservableCollection<ViewModel> ViewsToShow
     {
          ...
     }

     public void ShowFirstViewModel()
     {
          ViewsToShow.Add(new MyFirstViewModel());
     }
}

Чтобы связать все в вашем представлении, вы бы затем добавили datatemplate этим типам с их пользовательскими элементами управления (но это не привело бы к созданию экземпляров этих представлений, если бы они не были необходимы:

<Window ...>
     <Window.Resources>
          <DataTemplate DataType="{x:Type myViewModels:MyFirstViewModel}">
               <myViews:MyFirstView />
          </DataTemplate>

          <DataTemplate DataType="{x:Type myViewModels:MySecondViewModel}">
               <myViews:MySecondView />
          </DataTemplate>
     </Window.Resources>

     <ItemsControl ItemsSource="{Binding ViewsToShow}" />

</Window>

И для любых ViewModels, которые вы добавляете в "ViewsToShow", представление автоматически увидит это и шаблон в соответствующем представлении.Опять же, без создания его экземпляра до того, как это потребуется.

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


Вопрос о сохранении состояния возникает при использовании подхода с привязкой к данным.Решение здесь состоит в том, чтобы использовать вашу ViewModel в качестве состояния элемента управления и соответствующим образом спроектировать как ваши ViewModels, так и ваши представления.Вот пример, который позволяет вам менять местами ваши представления с помощью DataTemplating, но переключение туда и обратно сохраняет состояние.

Предположим, у вас есть настройка из последнего раздела с 2 viewmodels, в которых определены datatemplates.Давайте немного изменим MainViewModel:

public MyMainViewModel : ViewModel
{
     public RelayCommand SwapViewsCommand
     {
          ...
     }

     public ViewModel View
     {
          ...
     }
     private ViewModel _hiddenView;
     public MyMainViewModel()
     {
          View = new MyFirstViewModel();
          _hiddenView = new MySecondViewModel();
          SwapViewsCommand = new RelayCommand(SwapViewModels);
     }

     public void SwapViewModels()
     {
          var hidden = _hiddenView;
          _hiddenView = View;
          View = hidden;
     }
}

И несколько изменений в основном виде.Я опустил таблички с данными для краткости.

<Window ...>
     <!-- DataTemplates Here -->
     <Button Command="{Binding SwapViewsCommand}">Swap!</Button>
     <ContentControl Content="{Binding View}" />
</Window>

Вот и все.Секрет здесь в том, что я сохраняю ссылку на исходную модель представления.Таким образом, предположим, что в viewmodel есть свойство string и связанное с ним текстовое поле в DataTemplated usercontrol с двусторонняя привязка тогда состояние, по существу, будет сохранено.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top