Pergunta

No meu cenário eu tenho MainView + MainViewModel, UserControl1 + UserControl 2.No MainView tenho 2 botões rotulados:Button_ShowUserControl1 + Button_ShowUserControl2.Na parte inferior do MainView eu tenho um "ContentGrid" que leva/deve_tirar...cada UserControl.

Meu gol:

Quando Button_ShowUserControl1 é clicado UserControl1 é Visível e UserControl2 OU qualquer outro UserControl deve ser definido como Desabou.O mesmo é válido para Button_ShowUserControl2.

Meu problema:

1.) Como os UserControls devem ser carregados no início da aplicação, como posso colocá-los todos juntos em um "ContentGrid"?Na verdade isso não é possível...então, como posso tornar um UserControl visível enquanto o outro está no mesmo lugar/"ContentGrid" acabou de entrar em colapso?

2.) Como 1.) parece não ser possível, como posso instanciar todos os UserControls no início do aplicativo e torná-los apenas visíveis/recolhidos quando o respectivo botão é clicado?

3.) Como um UserControl possui uma propriedade Visibility = Visible/Hidden/Collapsed, como posso vincular a uma propriedade em um ViewModel retornar um valor como Collapsed?Eu só consegui obter um valor booleano como Visibility = false/true ?

Meu código de teste:

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

Sim, o código está errado, por isso pergunto aqui :)

Foi útil?

Solução

Você só tem duas coisas erradas com este código.

1) Você não pode definir a visibilidade de um controle de usuário diretamente...você tem que configurá-lo em um contêiner:

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

2) A visibilidade não é um valor booleano, é uma enumeração.Como tal, você precisará usar um conversor para converter de booleano em Visibilidade.Observar:

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

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

Deveria ser isso.Espero que isto ajude.

Há outras coisas sobre as quais você está deixando pistas que podem afetar a capacidade disso funcionar.Por exemplo, você não mostra o maior elemento do contêiner...você está agrupando tudo em um StackPanel?Se você estiver agrupando tudo em uma Grade, por exemplo, os controles irão sobrepor tudo em camadas.

Experimente essas mudanças que sugiro...isso deve aproximar você.


Editar:Outra ideia usando modelos de dados

Outra coisa que você pode fazer é ter um ViewModel exclusivo para cada uma dessas visualizações que deseja mostrar e ocultar:

public class MyFirstViewModel : ViewModel
{

}

public class MySecondViewModel : ViewModel
{

}

Então, a partir do seu ViewModel "pai" ou "principal", você mostra ou oculta as visualizações desejadas em virtude de tê-las em uma coleção de ViewModels:

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

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

Para conectar tudo em sua visualização, você criaria um modelo de dados desses tipos com seus controles de usuário (mas isso não faria com que essas visualizações fossem instanciadas, a menos que fossem necessárias:

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

E para qualquer ViewModels que você colocar em "ViewsToShow", a visualização verá automaticamente esse modelo na visualização apropriada.Novamente, sem instanciá-lo antes que seja necessário.

Provavelmente isso é um pouco mais limpo do que colocar tudo na Visualização e definir a visibilidade, mas dependeria de você ter um tipo de modelo de visualização exclusivo para cada visualização, o que pode não ser o caso.


A questão de salvar o estado surge ao usar a abordagem DataTemplated.A solução aqui é considerar seu ViewModel como o estado do controle e projetar seus ViewModels e Views de acordo.Aqui está um exemplo que permite trocar suas visualizações usando DataTemplating, mas alternar entre salva o estado.

Suponha que você tenha a configuração da última seção com 2 modelos de visualização que possuem modelos de dados definidos.Vamos mudar um pouco o 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;
     }
}

E algumas alterações na visualização principal.Omiti os DataTemplates por questões de brevidade.

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

É isso.O segredo aqui é que estou salvando a referência ao modelo de visualização original.Dessa forma, digamos que haja uma propriedade string em um viewmodel e uma caixa de texto associada no usercontrol DataTemplated com um ligação bidirecional então o estado será essencialmente salvo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top