使用MVVM在ButtonClick上折叠/可见UserControls - 没有交换机制 -

msf*_*boy 1 user-controls mvvm collapse

在我的情况我有一个的MainView + MainViewModel,+的UserControl1用户控件2.在我的MainView有2个按钮标记:Button_ShowUserControl1 + Button_ShowUserControl2.在的MainView下部我有一个"ContentGrid"这需要/ should_take ......每一个用户控件.

我的目标:

当Button_ShowUserControl1被点击的UserControl1可见UserControl2 任何其它用户控件必须设置倒塌.同样适用于Button_ShowUserControl2.

我的问题:

1.)由于UserControls应在应用程序启动时加载,如何将它们全部放在一个"ContentGrid"中?多数民众赞成实际上是不可能的...所以我怎么能让一个UserControl可见而另一个在同一个地方/"ContentGrid"刚刚崩溃?

2.)因为1.)似乎不可能如何在应用程序启动时实例化所有UserControl并使它们仅在单击相应的Button时可见/折叠?

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>
Run Code Online (Sandbox Code Playgroud)

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;
        }  
    }        
}
Run Code Online (Sandbox Code Playgroud)

是的,代码是错的,因此我在这里问:)

And*_*mes 8

这个代码只有2个问题.

1)您无法直接设置usercontrol的可见性...您必须在容器上设置它:

<Grid Visibility="Collapsed">
    <myControls:MyUserControl />
</Grid>
Run Code Online (Sandbox Code Playgroud)

2)可见性不是布尔值,它是枚举.因此,您需要使用转换器从布尔值转换为可见性.注意:

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

<Grid Visibility="{Binding ShouldShowUsercontrol1, Converter={StaticResource BoolToVis}}">
     <myControls:MyUserControl />
</Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

那应该是它.希望这可以帮助.

您还有其他一些可能影响其工作能力的线索.例如,您没有显示最大的容器元素......您是否将所有内容包装在StackPanel中?例如,如果要将所有内容包装在网格中,则控件将覆盖图层中的所有内容.

尝试这些改变我建议......它应该让你更接近.


编辑:使用数据模板的另一个想法

您可以做的另一件事是确保为要显示和隐藏的每个视图都有一个唯一的ViewModel:

public class MyFirstViewModel : ViewModel
{

}

public class MySecondViewModel : ViewModel
{

}
Run Code Online (Sandbox Code Playgroud)

然后,从"父"或"主"ViewModel,您可以通过将它们放在ViewModel集合中来显示或隐藏所需的视图:

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

     public void ShowFirstViewModel()
     {
          ViewsToShow.Add(new MyFirstViewModel());
     }
}
Run Code Online (Sandbox Code Playgroud)

要在视图中连接所有内容,您可以使用其用户控件对这些类型进行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>
Run Code Online (Sandbox Code Playgroud)

对于您放入"ViewsToShow"的任何ViewModel,视图将​​在相应的视图中自动查看该模板和模板.再次,不需要在需要之前实例化它.

这可能比在View和设置可见性中放置所有单个东西要清晰一些,但它取决于您为每个视图都有一个唯一的视图模型类型,可能不是这种情况.


使用DataTemplated方法时会出现保存状态的问题.这里的解决方案是将ViewModel作为控件的状态,并相应地设计ViewModel和Views.这是一个允许您使用DataTemplating交换视图的示例,但来回切换可以保存状态.

假设您具有上一部分的设置,其中包含已定义datatemplates的2个视图模型.让我们稍微改变一下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;
     }
}
Run Code Online (Sandbox Code Playgroud)

并对主要观点进行了一些修改.为简洁起见,我省略了DataTemplates.

<Window ...>
     <!-- DataTemplates Here -->
     <Button Command="{Binding SwapViewsCommand}">Swap!</Button>
     <ContentControl Content="{Binding View}" />
</Window>
Run Code Online (Sandbox Code Playgroud)

而已.这里的秘诀是我正在保存对原始视图模型的引用.这样,假设在viewmodel中有一个字符串属性,并且DataTemplated用户控件中的关联文本框具有双向绑定,那么状态将基本上被保存.