Binding

De Banane Atomic
Révision datée du 19 octobre 2021 à 21:48 par Nicolas (discussion | contributions) (→‎TemplateBinding)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigationAller à la recherche

Links

Objet métier ↔ objet graphique : INotifyPropertyChange

On incrémente la valeur Entier lors du click sur le bouton.
Comme la classe Numéro implémente INotifyPropertyChanged et que la propriété Entier raise l’évènement PropertyChanged lorsque sa valeur change, le TextBlock qui est bindé sur la propriété Entier est notifié du changement et peut mettre à jour la valeur Text.

Csharp.svg
public partial class MainWindow : Window
{
    public Numéro Numéro { get; set; }
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Numéro.Entier++;
    }

    public MainWindow()
    {
        InitializeComponent();

        Numéro = new Numéro();
        this.DataContext = this;
    }
}

public class Numéro : INotifyPropertyChanged
{
    private int _entier;
    public int Entier {
        get { return _entier; }
        set
        {
            _entier = value;
            OnPropertyChanged("Entier");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
Xaml.svg
<Window x:Class="WpfApplication.MainWindow" ... >
    <TextBlock Text="{Binding Path=Numéro.Entier}"></TextBlock>
    <Button Click="Button_Click">Click</Button>

Objet graphique ↔ objet graphique : Dependancy Property

Xaml.svg
<!-- Récupère la valeur Texte de tbx et modifie la valeur Texte du TextBlock -->
<TextBlock Text="{Binding ElementName=tbx, Path=Text, Mode=OneWay}"></TextBlock>
<TextBox x:Name="tbx"></TextBox>

<!-- Pousse la valeur Texte du TextBox et modifie la valeur Texte de tbk -->
<TextBlock x:Name="tbk"></TextBlock>
<TextBox Text="{Binding ElementName=tbk, Path=Text, Mode=OneWayToSource,
                        UpdateSourceTrigger=PropertyChanged}"></TextBox>
Les propriétés Text de TextBlock et TextBox sont des Dependancy Property, c'est à dire qu'elles implémentent INotifyPropertyChanged
Pour accéder à un control qui n'est pas dans le fichier xaml, utiliser FindAncestor.

Mode

Mode
OneWay Récupère une valeur de la Source pour modifier la Target
OneWayToSource Pousse une valeur de la Target pour modifier la Source
TwoWay Synchronisation
OneTime similaire à OneWay, mais ne met à jour l'UI qu'une seule fois à l'initialisation.
Target: élément (graphique) contenant le Binding
Source: élément ciblé par le binding (DataContext)

TemplateBinding

Permet de créer une liaison entre le template et l'élément templaté.

Xaml.svg
<Button Content="Bouton">
    <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
            <!-- liaison du Text de la TextBox avec le Content du Button -->
            <TextBlock Text="{TemplateBinding Content}"/>
        </ControlTemplate>
    </Button.Template>
</Button>

<Style BasedOn="{StaticResource {x:Type Button}}"
       TargetType="Button">
    <Setter Property="Background" Value="Blue" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <!-- link the Background to the one defined in the style -->
                <TextBlock Background="{TemplateBinding Background}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

TemplateBinding vs Binding TemplatedParent

Ce sont deux manières d'exprimer la même chose, mais Binding permet de modifier le Mode et l'UpdateSourceTrigger.

Xaml.svg
<TextBlock Text="{TemplateBinding Property=Content}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}"/>

RelativeSource

Xaml.svg
<TextBlock Text="{TemplateBinding Property=Content}"/>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}" />

<KeyBinding Key="Down" Command="{Binding Path=KeyboardCommand}"
                       CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Key}" />

Self

Créé un binding entre 2 propriétés du même control.

Xaml.svg
<Button Width="200" Height="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Width}">B1</Button>

TemplatedParent

Créé un binding entre une propriété du template et une propriété du control contenant le template.

Xaml.svg
<Button Content="B3">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Grid>
                <Ellipse Height="100" Width="150" 
                         Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" />
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
                           Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
            </Grid>
        </ControlTemplate>
    </Button.Template>
</Button>

FindAncestor

Remonte l'arborescence d'éléments visuels WPF pour trouver un type de Control.
Il est possible de spécifier un AncestorLevel qui correspond au nième Control rencontré du type spécifié.

UserControl.xaml
<UserControl x:Class="WpfApplication1.UserControl">
    <StackPanel>
        <Button Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window, AncestorLevel=1},
                                     Path=Background}">B2</Button>
    </StackPanel>
</UserControl>
Window.xaml
<Window x:Class="WpfApplication1.MainWindow"
        xmlns:WpfApplication="clr-namespace:WpfApplication"
        Background="Pink">
    <StackPanel>
        <WpfApplication:UserControl />
    </StackPanel>
</Window>
Path=Children[1] permet d'accéder à la 2ème balise filles. 1

PreviousData

Permet de se binder avec l'élément précédent dans une ItemsSource.
Avec Values={1, 2, 3, 4, 5, 6, 7, 8, 9}, on a:

1
2 (Previous was 1)
3 (Previous was 2)
Xaml.svg
<ListBox ItemsSource="{Binding Path=Values}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding}" FontWeight="Bold" />
                <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=PreviousData}, 
                                          TargetNullValue='', StringFormat=' (Previous was {0})'}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Source

Si la source du binding n'est pas spécifiée, c'est le DataContext qui est utilisé. Si celui-ci est null, c'est le premier DataContext non-null d'un parent qui sera utilisé.

Xaml.svg
<TextBlock Text="{Binding Source={StaticResource ResourceKey=MainVM}, Path=TextVM}"></TextBlock>

Multibinding

Xaml.svg
<UserControl.Resources>
    <ResourceDictionary>
        <conversion:ArrayOfBooleansToVisibilityConverter x:Key="AllBooleanTrueToVisibilityConverter" />

<Button.Visibility>
    <MultiBinding Converter="{StaticResource AllBooleanTrueToVisibilityConverter}"
                  FallbackValue="Collapsed">
        <Binding Path="Prop1" />
        <Binding Path="Prop2" />
    </MultiBinding>
ArrayOfBooleansToVisibilityConverter.cs
[ValueConversion(typeof(bool[]), typeof(Visibility))]
public class ArrayOfBooleansToVisibilityConverter : IMultiValueConverter
{
    public bool IsInverted { get; set; }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var result = values.All(v => object.Equals(v, true));

        if (this.IsInverted)
        {
            result = !result;
        }

        return result ? Visibility.Visible : Visibility.Collapsed;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

StringFormat

Xaml.svg
<TextBlock Text="{Binding Name, StringFormat=Name: {0}}" />
<TextBlock Text="{Binding Date, ConverterCulture='fr-FR', StringFormat=Date: {0:D}}" />