1

在我的场景中,我有一个 MainView + MainViewModel、UserControl1 + UserControl 2。在 MainView 中,我有 2 个按钮标记为:Button_ShowUserControl1 + Button_ShowUserControl2。在 MainView 的下部,我有一个“ContentGrid”,它采用/should_take...每个用户控件。

我的目标:

单击 Button_ShowUserControl1 时,UserControl1可见UserControl2 任何其他 UserControl 必须设置为Collapsed。同样适用于 Button_ShowUserControl2。

我的问题:

1.) 由于 UserControls 应在应用程序启动时加载,我如何将它们全部放在一个“ContentGrid”中?这实际上是不可能的......那么我怎样才能让一个 UserControl 可见,而另一个在同一个地方/“ContentGrid”刚刚折叠?

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

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

是的,代码是错误的,所以我在这里问:)

4

1 回答 1

9

这段代码只有两件事有问题。

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 中?例如,如果您将所有内容包装在 Grid 中,则控件将覆盖所有内容。

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


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

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

public class MyFirstViewModel : ViewModel
{

}

public class MySecondViewModel : ViewModel
{

}

然后从您的“父”或“主”视图模型中,您可以通过将视图模型放在集合中来显示或隐藏所需的视图:

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

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

要将视图中的所有内容连接起来,您可以使用它们的用户控件对这些类型进行数据模板化(但这不会导致这些视图被实例化,除非需要它们:

<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,视图将​​自动在适当的视图中看到该视图和模板。同样,无需在需要之前实例化它。

这可能比将所有东西都放在视图中并设置可见性要干净一些,但这取决于您对每个视图都有一个唯一的视图模型类型,但情况可能并非如此。


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

假设您有上一节中的设置,其中包含定义了数据模板的 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;
     }
}

并对主视图进行了一些更改。为简洁起见,我省略了 DataTemplates。

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

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

于 2010-01-20T16:26:28.147 回答