按钮上的折叠/可见用户控件单击MVVM - 无交换机制 -

时间:2010-01-19 21:31:17

标签: mvvm user-controls collapse

在我的场景中,我有一个MainView + MainViewModel,UserControl1 + UserControl 2。 在MainView中,我有2个标记为ButtonShowUserControl1 + Button_ShowUserControl2的按钮。 在MainView的下半部分,我有一个“ContentGrid”,需要/ should_take ...每个UserControl。

我的目标:

单击Button_ShowUserControl1 UserControl1 可见 UserControl2 任何其他UserControl必须设置为折叠即可。同样适用于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>

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;
        }  
    }        
}

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

1 个答案:

答案 0 :(得分:8)

此代码只有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,您可以通过在ViewModel集合中显示或隐藏所需的视图:

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>

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

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


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

假设你有最后一节的设置w / 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;
     }
}

主视图有一些变化。为简洁起见,我省略了DataTemplates。

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

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