Laden...

UpdateSourceTrigger von CustomControls updatet die Datenquelle nicht

Erstellt von Brendan vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.311 Views
B
Brendan Themenstarter:in
39 Beiträge seit 2006
vor 7 Jahren
UpdateSourceTrigger von CustomControls updatet die Datenquelle nicht

Ich arbeite derzeit mit WPF und C# an einer WPF-Applikation, in der die Controls und ihre Bindings zur Laufzeit in Code-behind erzeugt werden.
Das WPF-Window auf dem ich die Controls erzeuge, hat ein ViewModel mit einer DataTable (die in DataContext eingestellt wird) und - am Fuss des WPF-Window - einen DataGrid, der an den DefaultView der DataTable gebunden ist.

Zuerst verwendete ich zum Erzeugen von Controls die normalen WPF-Controls, z.B. die TextBox und die CheckBox.
In deren Bindings setze ich UpdateSourceTrigger auf "PropertyChanged" wie hier:


    Binding controlBinding = new Binding();
    controlBinding.Source =

 ViewModelContainer.viewmodel.ApplicationDataSet.Tables[BindingSource].DefaultView;
    controlBinding.Path = new PropertyPath("[0][" + BindingPath + "]");
    controlBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

Wenn ich nach der Generierung der Controls den Text der TextBox änderte (ohne sie zu verlassen) oder die Checkbox an-/abhakte, konnte ich die Änderungen im DataGrid augenblicklich sehen.

Nun aber verwende ich CustomControls die von den normalen WPF-Controls erben (um sie um neue Properties zu erweitern) und stelle leider fest, dass die UpdateSourceTrigger-Funktionalität nicht mehr arbeitet.
Wenn ich den Text der TextBox ändere oder die Checkbox an-/abhake, sehe ich keine Änderung im DataGrid und damit ist auch keine Änderung in der gebundenen DataTable vorhanden.

Wie man oben an meinem Codebeispiel sehen kann, tue ich bei meinen CustomControls Source bzw. DataContext auf die DataTable setzen.

Ich vermute, dass ich an den Definitionen der CustomControls im Generic.xaml etwas ändern muss, aber was?

Hier die Definitionen der CustomTextBox und der CustomCheckBox:


    <!--Style for the CustomControl CustomTextBox-->
    <Style TargetType="{x:Type local:CustomTextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomTextBox}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <TextBox Text="{TemplateBinding Text}"
                                 TextWrapping="Wrap"
                                 HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                 VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                 ContextMenu="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:CustomTextBox}},
                            Path=ContextMenu}"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <!--Style for the CustomControl CustomCheckBox-->
    <Style TargetType="{x:Type local:CustomCheckBox}" BasedOn="{StaticResource {x:Type CheckBox}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomCheckBox}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <CheckBox IsChecked="{TemplateBinding IsChecked}"
                                  HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                            <TextBlock Text="{TemplateBinding Text}"
                                       TextWrapping="Wrap"
                                       TextAlignment="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type CheckBox}},
                                Path=HorizontalContentAlignment, Converter={StaticResource h2tAlignmentConverter}}"
                                       TextDecorations="{TemplateBinding TextDecorations}"/>
                        </CheckBox>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Vielen Dank im voraus!

Patrick

Die Lösung ist immer ganz einfach.
Nur der Weg dorthin ist schwierig!

5.299 Beiträge seit 2008
vor 7 Jahren

Wirklich was zu sagen kann ich nicht. Ausser: Ich würde so etwas nur machen, wenns absolut tödliche Gründe gäbe, die mich dazu zwingen, so etwas zu verzapfen:

..., an einer WPF-Applikation, in der die Controls und ihre Bindings zur Laufzeit in Code-behind erzeugt werden. Das ist so ungewöhnlich, und konträr zum MVVM-Pattern, dass ich fast vermute, niemand kann dir da mit persönlichen Erfahrungen zur Seite stehen.

Der frühe Apfel fängt den Wurm.

B
Brendan Themenstarter:in
39 Beiträge seit 2006
vor 7 Jahren

Ist mir klar, dass es ein sehr seltener Fall ist.
Leider muss ich es so machen, da wir an einem WPF-Tool arbeiten, mit dem man sich seine Masken selbst zusammenstellen kann.
Ist hie und da etwas tricky, aber bisher ging es eigentlich.
Nun ist halt das Problem mit dem Update der Datenquelle durch die CustomControls aufgetaucht.
Mit den normalen Controls ging es.
Also müsste es mit den CustomControls auch gehen, nur wie ist die spannende Frage.

Die Lösung ist immer ganz einfach.
Nur der Weg dorthin ist schwierig!

W
955 Beiträge seit 2010
vor 7 Jahren

Hi,
hast du zur Differentialdiagnostik mal deine CustomControls mit normalen Bindings getestet, welche also nicht zur Laufzeit gesetzt werden?

B
Brendan Themenstarter:in
39 Beiträge seit 2006
vor 7 Jahren

Hallo witte,

Nicht wirklich. Lediglich in einem XAML habe ich mal die Controls eingefügt und die Text-Eigenschaft fix gesetzt.
Aber inzwischen habe ich in einem anderen Forum einen Hinweis gefunden, was schief gelaufen ist.
Mehr dazu in meiner selbst verfassten Antwort.

Die Lösung ist immer ganz einfach.
Nur der Weg dorthin ist schwierig!

B
Brendan Themenstarter:in
39 Beiträge seit 2006
vor 7 Jahren

Das Problem besteht darin, dass es im Generic.xaml innerhalb der CustomTextBox eine innere TextBox gibt, deren Text-Eigenschaft zwar die Einstellung der Text-Eigenschaft des CustomControls übernimmt, aber wenn man den Text der inneren TextBox ändert, dringt dies nicht zur CustomTextBox durch.
Hierzu muss man im Code des CustomControls ein wenig nachhelfen.

Zuerst müssen wir die innere TextBox finden und in einem Platzhalter merken.


        /// <summary>
        /// Gets or sets the inner textbox of this customcontrol.
        /// </summary>
        public TextBox InnerTextBox { get; set; }

        /// <summary>
        /// Overrides the method OnApplytemplate.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            var innertextbox = this.GetTemplateChild("innertextbox");

            if (innertextbox != null && InnerTextBox == null)
            {
                InnerTextBox = innertextbox as TextBox;
                InnerTextBox.TextChanged -= InnerTextBoxTextChanged;
                InnerTextBox.TextChanged += InnerTextBoxTextChanged;
            }
        }

Und in dem EventHandler zum TextChanged-Event holen wir den Text der inneren TextBox und stellen ihn in den Text der CustomTextBox.


        /// <summary>
        /// The Text-property of the inner textbox was changed.
        /// So we have to change the Text-property of the CustomControl too -> update datasource.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The eventarguments.</param>
        private void InnerTextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            // sender = InnerTextBox
            string mytext = ((TextBox)sender).Text;
            this.Text = mytext;
        }

Zugegeben, es ist nicht gerade schön, aber es funktioniert.
Sollte jemand noch eine bessere Lösung haben, immer her damit!

Die Lösung ist immer ganz einfach.
Nur der Weg dorthin ist schwierig!

B
Brendan Themenstarter:in
39 Beiträge seit 2006
vor 7 Jahren

Okay, für das CustomControl, das von der TextBox erben tut, gibt es tatsächlich einen besseren Weg als Hardcoding.
Dazu muss man das Binding des Text-Property der inneren TextBox ändern:


<TextBox x:Name="innerTextBox"
    Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}"
    [...]/>

Die Lösung ist immer ganz einfach.
Nur der Weg dorthin ist schwierig!

P
157 Beiträge seit 2014
vor 7 Jahren

Hallo,

ich frag mal ganz provokant, wieso habt ihr eine textbox in einer textbox ? das das ist eine etwas ... holprige lösung...wenn ihr ne textbox selbst macht, dann erbt doch von textbox und baut das template korrekt, dann treten solche probleme gar nicht auf (gibt n trick sich die kompletten styles per reflection aus der controls zu ziehen, gibt sicherlich einige beispiele wenn man sucht)

das selbe spielchen mit der checkbox...

Es gibt da einen unterschied, zwischen customcontrol und usercontrol, willst du ein steuerelement auf einer "atomaren" ebene so erweitern dass sich das vererben lohnt, dann vererbe. Wenn die Anforderung ein "kombiniertes" steuerelement ist, benutz ein Usercontrol.

Bei den Bindings muss man immer darauf achten, dass die Bindingkette von A nach B konsequent durchzieht...ist wie mit so domino-steinchen..das eine schubst das andere an.

In der Custom-CheckBox verwendest du einen TextBlock als Content für eine Checkbox, sowas sollte man vielleicht noch mal überlegen, das geht auf dauer stark in die performance, da du anstatt einem controll auf einmal 3 hast .. deins, die checkbox und die textbox.

wenn ihr nur bestimmte fähigkeiten eines controls erweitern wollt, könnt ihr attached properties verwenden oder behaviors, das ist wesentlich einfacher.

Noch ein kleiner Hinweis...Blend ist ein sehr gutes werkzeug für das zusammenstellen von UI's und ziemlich einfach zu benutzen, das gibt seltsamerweise viele firmen die versuchen sowas nachzuprogrammieren...sich ins programm einarbeiten geht 1000 mal schneller.

vg

Wenn's zum weinen nicht reicht, lach drüber!