59 votes

Lier une partie seulement de la propriété marginale d'un contrôle WPF

J'ai ça :

<TabControl Margin="0,24,0,0">...</TabControl>

Je veux lier seulement le "Top" du TabControl, ce que je ferais intuitivement de cette façon :

<TabControl Margin="0,{Binding ElementName=TheMenu, Path=Height},0,0">
 ...
</TabControl>

Comment dois-je m'y prendre ?

57voto

Markus Points 2472

Avez-vous essayé d'utiliser un convertisseur comme celui-ci ?

en VB.Net

Public Class MarginConverter
  Implements IValueConverter

  Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    Return New Thickness(0, CDbl(value), 0, 0)
  End Function

  Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
    Return Nothing
  End Function
End Class

Ou en C#

public class MarginConverter : IValueConverter
{

    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return new Thickness(0, System.Convert.ToDouble(value), 0, 0);
    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

XAML

<Window.Resources>
    <local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
</Window.Resources>
<Grid>
    <StackPanel>
        <Slider Name="Slider1"></Slider>
        <TabControl Name="TabControl" Margin="{Binding ElementName=Slider1, Path=Value, Converter={StaticResource marginConverter}}">
            <Button>Some content</Button>
        </TabControl>
    </StackPanel>
</Grid>

Edit :
Utilisation d'un MultiConverter

Il est également possible d'obtenir les quatre valeurs pendant l'exécution et d'utiliser un MultiValueConverter. La Top-Property de l'Thickness-Object n'est pas un Dependency-Object. Par conséquent, vous ne pouvez pas définir de liaison avec elle (à moins que votre source ne soit pas un Dependency-Object).

XAML

<Window.Resources>
    <local:MarginConverter x:Key="marginConverter"></local:MarginConverter>
    <local:MultiMarginConverter x:Key="multiMarginConverter"></local:MultiMarginConverter>
</Window.Resources>
<Grid>
    <StackPanel>
        <Slider Name="Slider1"></Slider>
        <Slider Name="Slider2"></Slider>
        <Slider Name="Slider3"></Slider>
        <Slider Name="Slider4"></Slider>
        <TabControl Name="TabControl">
            <TabControl.Margin>
                <MultiBinding Converter="{StaticResource multiMarginConverter}">
                    <Binding ElementName="Slider1" Path="Value"></Binding>
                    <Binding ElementName="Slider2" Path="Value"></Binding>
                    <Binding ElementName="Slider3" Path="Value"></Binding>
                    <Binding ElementName="Slider4" Path="Value"></Binding>
                </MultiBinding>
            </TabControl.Margin>
            <Button>Some content</Button>
        </TabControl>
    </StackPanel>
</Grid>

... et c#

  class MultiMarginConverter : IMultiValueConverter
  {
    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return new Thickness(System.Convert.ToDouble(values[0]),
                           System.Convert.ToDouble(values[1]),
                           System.Convert.ToDouble(values[2]),
                           System.Convert.ToDouble(values[3]));
    }

    public object[] ConvertBack(object value, System.Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      return null;
    }
  }

Edit(2) Reliure inversée :
Je ne suis pas sûr que cela vous rende heureux. A mon humble avis, j'essaierais d'éviter cela, mais bon... Si votre source est une propriété de dépendance, vous pouvez la lier à la marge :

<Slider Name="Slider5" Minimum="-99" Maximum="0" Value="{Binding ElementName=TabControl, Path=Margin.Top, Mode=OneWayToSource}"></Slider>

Mais j'ai quelques effets avec ça.
L'astuce consiste à ne pas lier une partie de la marge de votre contrôle d'onglet à "autre chose", mais à lier "autre chose" à la marge de votre contrôle d'onglet et à spécifier le mode de liaison (Binding-Mode). OneWayToSource .

28voto

loop Points 2534

En fait, Margin d'un contrôle est de Thickness Type. Nous pouvons donc le lier à Property if type Thickness.

 public Thickness LeftMargin { get; set; }

et vous pouvez également définir une partie d'un objet d'épaisseur. Par exemple.

 LeftMargin = new Thickness(20,0,0,0);

et en Xaml nous pouvons lier cette propriété directement à la propriété margin de n'importe quel élément comme ceci

 <TextBlock Text="Some Text"  Margin="{Binding LeftMargin}"  />

1 votes

Je pense que si vous avez seulement un à lier, c'est la bonne approche. Sinon, si vous avez besoin de valeurs différentes pour chaque côté de la marge du contrôle, d'autres options sont nécessaires.

2 votes

@heltonbiker Je pense que c'est parfaitement bien, vous pouvez définir la gauche, la droite, le haut et le bas individuellement, avec des valeurs différentes.

0 votes

@CularBytes Je suppose qu'il voulait dire, si vous avez besoin de lier chaque côté à des propriétés différentes. Cela ne peut pas être fait avec un seul Margin attribut. MultiBinding est alors la seule approche, pour autant que je sache.

12voto

bugged87 Points 764

Vous pourriez essayer quelque chose comme este réponse à une autre question.

La solution utilise une propriété attachée qui permet d'utiliser un XAML comme le suivant :

<Button ap:MoreProps.MarginRight="10" />

La propriété attachée est également soutenue par un DependencyObject, de sorte que la liaison de données fonctionne.

1 votes

Il s'agit de la meilleure réponse, qui permet des liaisons cumulatives indépendantes des marges, et la seule qui offre cette fonctionnalité dans tous les fils de discussion que j'ai lus à ce sujet.

1 votes

Bien mieux que la réponse du haut, car c'est une approche naturelle et logique. Merci !

5voto

adaraz Points 117

J'ai utilisé cette solution de contournement pour la marge gauche uniquement avec StackPanel. L'avantage est que vous n'avez pas besoin de convertisseur.

<DockPanel VerticalAlignment="Top">
  <TextBlock Name="tbkFulltextCaption"
             Text="Static Caption:"
             DockPanel.Dock="Left" />
  <StackPanel Orientation="Horizontal"
              DockPanel.Dock="Bottom">
      <FrameworkElement Name="feLeftMargin"
                        Width="{Binding Width, ElementName=tbkFulltextCaption, Mode=OneWay}" />
      <TextBlock Text="(some text with margin of tbkFulltextCaption.Width)"
                 Name="tbkUnderNonsense" 
                 FontSize="8"                                       
                 Foreground="Gray">
      </TextBlock>
  </StackPanel>
  <TextBox Name="tbFulltextSearch" />
</DockPanel>

aperçu

4voto

LoveRight Points 909

D'après votre code, j'imagine que votre menu et votre tabControl peuvent se chevaucher, vous devez donc utiliser une marge pour les séparer. Je pense que cette pratique ressemble à la mise en page CSS à deux colonnes.

Pour en revenir au sujet, je pense que vous pouvez appliquer TranslateFransform t TabControl.RenderTransform . Vous pouvez lier Y propriété.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X