Ocultar linha da grade no WPF

94

Eu tenho um formulário WPF simples com um Griddeclarado no formulário. Isso Gridtem um monte de linhas:

<Grid.RowDefinitions>
    <RowDefinition Height="Auto" MinHeight="30" />
    <RowDefinition Height="Auto" Name="rowToHide" />
    <RowDefinition Height="Auto" MinHeight="30" />
</Grid.RowDefinitions>

A linha nomeada rowToHidecontém alguns campos de entrada e quero ocultar essa linha depois de detectar que não preciso desses campos. É bastante simples definir apenas Visibility = Hiddenpara todos os itens na linha, mas a linha ainda ocupa espaço no Grid. Tentei definir Height = 0os itens, mas não pareceu funcionar.

Você pode pensar assim: você tem um formulário, nele há uma lista suspensa que diz "Tipo de pagamento" e, se a pessoa selecionar "Dinheiro", você deseja ocultar a linha que contém os detalhes do cartão. Não é uma opção iniciar o formulário com este oculto já.

Richard
fonte
1
veja esta dica sobre Visibilidade sendo um sistema de 3 estados (no tópico de dicas do WPF): stackoverflow.com/questions/860193/wpf-simple-tips-and-tricks/…
Metro Smurf
Coisas brilhantes ... Se você colocasse como uma resposta, eu marcaria que ...
Richard
Dê uma olhada nesta dica também: social.msdn.microsoft.com/Forums/en-US/wpf/thread/…
Domokun

Respostas:

88

Row não tem uma propriedade Visibility, então como outros disseram, você precisa definir a Height. Outra opção é usar um conversor, caso você precise dessa funcionalidade em várias visualizações:

    [ValueConversion(typeof(bool), typeof(GridLength))]
    public class BoolToGridRowHeightConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return ((bool)value == true) ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {    // Don't need any convert back
            return null;
        }
    }

E então, na visualização apropriada <Grid.RowDefinition>:

<RowDefinition Height="{Binding IsHiddenRow, Converter={StaticResource BoolToGridRowHeightConverter}}"></RowDefinition>
Padrão de teste
fonte
10
UpVoted - os conversores permitem que tudo isso seja declarativo no Xaml. Eu geralmente odeio usar code-behind para mexer com coisas visuais.
Allen de
1
Isso é muito útil e pode ser facilmente estendido. Eu sugiro chamá-lo BoolToGridLengthConvertere adicionar um VisibleLength-Property, para retornar (bool)value == true. É assim que você também pode reutilizá-lo com Autoqualquer valor fixo.
LuckyLikey
1
Ótima resposta. Suponho que você quis dizer IsDisplayedRow, não IsHiddenRow.
NielW
72

A melhor e mais limpa solução para recolher linhas ou colunas é usar um DataTrigger para, no seu caso:

<Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" MinHeight="30" />
      <RowDefinition Name="rowToHide">
        <RowDefinition.Style>
          <Style TargetType="{x:Type RowDefinition}">
            <Setter Property="Height" Value="Auto" />
            <Style.Triggers>
              <DataTrigger Binding="{Binding SomeBoolProperty}" Value="True">
                <Setter Property="Height" Value="0" />
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </RowDefinition.Style>
      </RowDefinition>
      <RowDefinition Height="Auto" MinHeight="30" />
    </Grid.RowDefinitions>
  </Grid>
Lukáš Koten
fonte
5
Eu gosto dessa abordagem porque você não precisa de código C # adicional.
user11909
1
Não se esqueça de implementar INotifyPropertyChangedem seu código para que funcione quando SomeBoolPropertyfor alterado :).
Benichka
55

Você também pode fazer isso referenciando a Linha na Grade e alterando a Altura da própria linha.

XAML

<Grid Grid.Column="2" Grid.Row="1" x:Name="Links">
   <Grid.RowDefinitions>
      <RowDefinition Height="60" />
      <RowDefinition Height="*" />
      <RowDefinition Height="*" />
      <RowDefinition Height="80" />
   </Grid.RowDefinitions>
</Grid>

VB.NET

If LinksList.Items.Count > 0 Then
   Links.RowDefinitions(2).Height = New GridLength(1, GridUnitType.Star)
Else
   Links.RowDefinitions(2).Height = New GridLength(0)
End If

Embora o recolhimento dos elementos dentro da grade também funcione, isso é um pouco mais simples se você tiver muitos itens na grade que não tenham um elemento envolvente que possa ser reduzido. Isso seria uma boa alternativa.

TravisPUK
fonte
2
Isso também tem a vantagem de trabalhar com linhas que usam a notação em estrela!
Johny Skovdal
1
Fazer isso no código é a solução mais clara e legível. Talvez adicione um comentário depois de RowDefinition, como<RowDefinition Height="*" /><!-- Height set in code behind -->
Kay Zed
2
Não acho que esta seja a solução mais clara e legível, pois o código funcional é dividido em dois arquivos separados. Na verdade, tudo pode ser feito com XAML puro - veja minha resposta.
Lukáš Koten
Minhas necessidades eram um pouco diferentes e em C #, mas este exemplo me indicou a direção certa. Obrigado!
nrod
30

Para referência, Visibilityé uma enumeração System.Windows.Visibility de três estados :

  • Visível - O elemento é renderizado e participa do layout.
  • Reduzido - o elemento é invisível e não participa do layout. Efetivamente dando a ele altura e largura 0 e se comportando como se ele não existisse.
  • Oculto - o elemento é invisível, mas continua a participar do layout.

Veja esta dica e outras dicas no tópico de dicas e truques do WPF .

Metro Smurf
fonte
1
Definir todos os itens na linha como Visibility. Collapsed funcionou, obrigado.
Richard,
1
Eu votei contra isso porque acho que a resposta de @ TravisPUK contém uma solução mais clara e óbvia.
padrão de
11
@testpattern - downvotes são normalmente usados ​​para respostas incorretas. Se a outra resposta for melhor, basta votar a favor.
Metro Smurf de
6
@MetroSmurf justo o suficiente. Provavelmente, sua resposta não está correta porque RowDefinition não tem uma propriedade para Visibility. TravisPUK mostra como ocultar uma linha e essa deve ser a resposta aceita.
testpattern de
8

Em vez de mexer na linha da grade, você pode definir a propriedade Visibility dos controles (campos na linha) como "Collapsed". Isso garantirá que os controles não ocupem nenhum espaço e se você tiver Grid Row Height = "Auto", a linha será ocultada, pois todos os controles na linha têm Visibility = "Collapsed".

<Grid>
       <Grid.RowDefinitions>
         <RowDefinition Height="Auto" />
         <RowDefinition Height="Auto" Name="rowToHide" />
       </Grid.RowDefinitions>

   <Button Grid.Row=0 Content="Click Me" Height="20">
       <TextBlock Grid.Row=1 
Visibility="{Binding Converter={StaticResource customVisibilityConverter}}" Name="controlToHide"/>

</Grid>

Este método é melhor porque a Visibilidade dos controles pode ser associada a alguma propriedade com a ajuda de um Conversor.

user3726565
fonte
7

Basta fazer isso:
rowToHide.Height = new GridLength(0);

se você usar, visibility.Collapseentão, você terá que defini-lo para cada membro da linha.

NOME DO USUÁRIO
fonte
6

Defina a visibilidade do conteúdo da linha como em Visibility.Collapsedvez de Oculto. Isso fará com que o conteúdo pare de ocupar espaço e a linha encolherá de forma adequada.

Reed Copsey
fonte
1
Eu vi em outro lugar que alguém mencionou a visibilidade da linha. Mas o Row não tem um estado de visibilidade? Configurar todos os itens na linha como Visibility.Collapsed funcionou.
Richard
5
@Richard: Você não pode definir RowDefinition.Visibility, pois não é um UIElement - mas você pode colocar todo o seu conteúdo para a linha (ou cada coluna dentro da linha) em um único contêiner e definir a visibilidade desse contêiner.
Reed Copsey
1
E se a linha da grade não tiver nenhum conteúdo, mas uma altura fixa? Existe uma maneira conveniente de mostrar / ocultar?
kevinarpe
4

Tive uma ideia semelhante ao herdar RowDefinition (apenas por interesse)

public class MyRowDefinition : RowDefinition
{
    private GridLength _height;

    public bool IsHidden
    {
        get { return (bool)GetValue(IsHiddenProperty); }
        set { SetValue(IsHiddenProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsHidden.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsHiddenProperty =
        DependencyProperty.Register("IsHidden", typeof(bool), typeof(MyRowDefinition), new PropertyMetadata(false, Changed));

    public static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var o = d as MyRowDefinition;
        o.Toggle((bool)e.NewValue);
    }

    public void Toggle(bool isHidden)
    {
        if (isHidden)
        {
            _height = this.Height;
            this.Height = new GridLength(0, GridUnitType.Star);
        }                                                     
        else
            this.Height = _height;
    }          
}

Agora você pode usá-lo da seguinte forma:

 <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <my:MyRowDefinition Height="4*" IsHidden="false" x:Name="RowToHide" />
        <RowDefinition Height="*" />
        <RowDefinition Height="60" />
    </Grid.RowDefinitions>

e alternar com

RowToHide.IsHidden = !RowToHide.IsHidden;
Matt
fonte