3 votes

WPF - Impossible de lier les propriétés du contrôle dans ContentPresenter au contrôle parent

J'ai un contrôle personnalisé qui est utilisé dans un formulaire, ce contrôle combine une étiquette et une zone de texte. Lorsque le formulaire est en mode lecture seule, le contrôle est modifié pour afficher une étiquette au lieu de la zone de texte. Pour ce faire, j'ai défini un modèle de contenu pour mon contrôle, comme suit :

<Style TargetType="{x:Type Controls:FormFieldControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="200"/>
                    </Grid.ColumnDefinitions>

                    <AdornerDecorator Grid.Column="1"
                                      Visibility="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, 
                                                   Path=IsReadOnly, 
                                                   Converter={StaticResource BooleanToInverseVisibilityConverter}, 
                                                   Mode=OneWay}">
                        <ContentPresenter Name="ContentPresenter" />
                    </AdornerDecorator>

                    <AdornerDecorator Grid.Column="1"
                                      Visibility="{Binding RelativeSource={RelativeSource AncestorType=FormFieldControl}, 
                                      Path=IsReadOnly, Converter={StaticResource BooleanToVisibilityConverter}, 
                                      Mode=OneWay}">
                        <ContentPresenter Name="ReadOnlyContentPresenter" />
                    </AdornerDecorator>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

ReadOnlyContentPresenter est une étiquette à laquelle on a appliqué le style suivant :

<Style x:Key="ReadOnlyFormFieldControl" TargetType="{x:Type Label}">
    <Setter Property="Width" Value="400" />
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, 
                               Path=ReadOnlyWidth, 
                               Converter={StaticResource IsSetConverter}}"
                     Value="True">
            <Setter Property="Width" 
                    Value="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, Path=ReadOnlyWidth}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Cela fonctionne bien pour environ 80 contrôles, mais lorsque j'ajoute d'autres contrôles au formulaire, la liaison dans le style de l'étiquette ne trouve pas la source (erreur 4) :

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyProject.Controls.FormFieldControl', AncestorLevel='1''. BindingExpression:Path=ReadOnlyWidth; DataItem=null; target element is 'Label' (Name=''); target property is 'ReadOnlyWidth' (type 'Double?')

La même chose se produit avec le style appliqué à la partie modifiable du contrôle. Je pense que le problème est lié à la quantité de contrôles traités, si je déplace le code du contrôle no. 81 (défaillant) juste au-dessus du contrôle no. 80 (qui fonctionne), la liaison dans le contrôle n° 81 fonctionne maintenant, mais celle dans le contrôle n° 80 ne fonctionne pas. 81 fonctionne maintenant, mais celle du contrôle n° 80 échoue. 80 échoue. J'ai fait cela pour plusieurs contrôles, et le comportement est cohérent.

Je n'ai toujours pas compris comment les AdornerDecorator fonctionne exactement, et en enquêtant j'ai trouvé ceci question qui mentionne un problème avec le fait d'avoir plus de 144 parures dans un adornerlayer, j'ai donc supprimé le fichier AdornerDecorator dans mon code, pour constater que le problème de liaison qui se produisait pour les contrôles n'était plus. 81 et plus se produit maintenant pour tous mes contrôles FormFieldControl 's.

Finalement, j'ai décidé d'essayer avec ContentControl au lieu de ContentPresenter dans mon modèle de contrôle. Cela fonctionne bien pour tous les contrôles du formulaire. Je sais que ContentPresenter est une forme allégée de ContentControl et qu'il est plus approprié de l'utiliser à l'intérieur d'un modèle de contenu que l'option ContentControl est. Cependant, je ne comprends pas bien pourquoi les liaisons échouent lorsque l'on utilise un fichier ContentPresenter et de travailler en utilisant un ContentControl .

Je trouve également étrange que seul un ensemble limité de commandes fonctionne et que, lorsqu'on n'utilise pas l'AdornerDecorator, aucune d'entre elles ne trouve la source de liaison.

Quelqu'un a-t-il rencontré quelque chose de similaire ? Est-ce que je fais quelque chose de mal avec l'outil ContentPresenter ? Ou si c'est un comportement attendu, quelqu'un pourrait-il m'aider à comprendre ce qui se passe ?

Toute idée est la bienvenue.

0voto

Benjamin Gale Points 5390

Votre question n'indique pas clairement si l'étiquette affiche simplement ce qui se trouve dans la zone de texte ou si elle doit afficher une étiquette personnalisée, mais si c'est le cas, je pense que vous devriez simplement utiliser la fonction IsReadOnly comme déjà mentionné.

Vous pouvez définir une ControlTemplate qui masque l'arrière-plan et les bordures de l'écran TextBox lorsqu'il n'est lu que pour donner l'impression qu'il s'agit juste d'une Label .

Essayez cet exemple simple :

<StackPanel>
    <StackPanel.Resources>
        <Style x:Key="{x:Type TextBox}"
               TargetType="{x:Type TextBoxBase}">
            <Setter Property="SnapsToDevicePixels"
                    Value="True" />
            <Setter Property="OverridesDefaultStyle"
                    Value="True" />
            <Setter Property="KeyboardNavigation.TabNavigation"
                    Value="None" />
            <Setter Property="FocusVisualStyle"
                    Value="{x:Null}" />
            <Setter Property="MinWidth"
                    Value="120" />
            <Setter Property="MinHeight"
                    Value="20" />
            <Setter Property="AllowDrop"
                    Value="true" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBoxBase}">
                        <Border Name="Border"
                                CornerRadius="2"
                                Padding="2"
                                Background="AliceBlue"
                                BorderBrush="Black"
                                BorderThickness="1">
                            <ScrollViewer Margin="0"
                                          x:Name="PART_ContentHost" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsReadOnly"
                                     Value="True">
                                <Setter TargetName="Border"
                                        Property="Background"
                                        Value="Transparent" />
                                <Setter TargetName="Border"
                                        Property="BorderBrush"
                                        Value="Transparent" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </StackPanel.Resources>
    <TextBox Margin="50,25"
             Name="tb" />

    <Button Content="Switch modes"
            Margin="50,25"
            Click="Button_Click" />
</StackPanel>

Vous aurez besoin du gestionnaire de clic de bouton suivant pour activer le mode lecture seule :

private void Button_Click(object sender, RoutedEventArgs e)
{
    tb.IsReadOnly = !tb.IsReadOnly;
}

Voici le résultat lorsque le TextBox est modifiable :

enter image description here

Et c'est ce que le TextBox lorsqu'il est en lecture seule.

enter image description here

0voto

Cœur Points 1538

Je pense que la raison pourrait être celle-ci :

var contentPresenter = new ContentPresenter { Content = new Button() };
var contentControl = new ContentControl { Content = new Button() };
if ((contentPresenter.Content as FrameworkElement).Parent == null)
    Debug.WriteLine("ContentPresenter won't let you get ancestors");
if ((contentControl.Content as FrameworkElement).Parent != null)
    Debug.WriteLine("ContentControl will let you get ancestors");

Les deux lignes sont affichées, ce qui signifie que ContentPresenter est l'un des rares FrameworkElement à ne pas définir correctement la propriété "Parent".

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