Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timing problem in WPF data binding in styles #9096

Open
heckl opened this issue May 11, 2024 · 4 comments
Open

Timing problem in WPF data binding in styles #9096

heckl opened this issue May 11, 2024 · 4 comments
Labels
Investigate Requires further investigation by the WPF team.

Comments

@heckl
Copy link

heckl commented May 11, 2024

Description

I wanted to have data validation at once in WPF. At the same time I wanted to detect LostFocus, this is the time I modify the DB. Moreover, I needed this in a DataGrid. I introduced a temp property. I use the PropertyChanged at the TempName so the validation happens at once and used the event handler at LostFocus. The event handler copied the TempName to the Name and triggered the DB change. This method worked.

I wanted to use Behavior instead of event handler.

FinalizeNameCommand copies the TempName to the Name. Visual Studio says there is a Binding problem at FinalizeNameCommand though the binding works.

Capture

The binding error says the DataContext is null, it should be the object behind the selected row, Person. FinalizeNameCommand
is in that object and it is executed as expected. It seems the Binding is evaulated before the DataContext is set, that is why an error is shown. Later the DataContext is set and the binding is reevaulated (?), that is why FinalizeNameCommand works.

The problem is not only a wrong error message. This causes other problems in the code later.

Reproduction Steps

https://www.dropbox.com/scl/fi/pugbf1mxo72yjv64sqt8x/ImmediateValidationBindingTest.zip?rlkey=cflx26ayz6kusz3vino1mf90l&dl=0

Download the solution.
Run the program
See the binding error
Modify a name and see that FinalizeNameCommand is executed

Expected behavior

There should be no binding error

Actual behavior

There is binding error

Regression?

No response

Known Workarounds

use events instead of behavior

Impact

No response

Configuration

.net8
windows
x64

Other information

No response

@Kuldeep-MS Kuldeep-MS added the Investigate Requires further investigation by the WPF team. label May 13, 2024
@miloush
Copy link
Contributor

miloush commented May 15, 2024

Local copy of the repro: ImmediateValidationBindingTest.zip
Also it is helpful to include the actual error text, which is

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=FinalizeNameCommand; DataItem=null; target element is 'TextBoxLostFocusBehavior' (HashCode=26534308); target property is 'LostFocusCommand' (type 'ICommand')

It is not entirely clear to me what you are trying to achieve, but a temp property sounds like an extreme measure. So you are trying to update the DB before the row or even cell is committed, even if it has validation errors? Can you not use CellEditEnding event?

As far as the error goes, it also seems correct, it happens when the element is created but not added to the tree yet, and at that point it is also true that DataContext is null.

What "other problems in the code later" this causes?

@heckl
Copy link
Author

heckl commented May 16, 2024

Dear @miloush
I want immediate validiton, so when I press '/' an error should be displayed at once. But I only want to refresh the db when I finished editing the cell (when I leave the cell).

                <DataGridTextColumn.EditingElementStyle>
                    <Style TargetType="{x:Type TextBox}">
                        <Setter Property="local:BehaviorInStyleAttacher.Behaviors">
                            <Setter.Value>
                                <collections:ArrayList>
                                    <local:TextBoxLostFocusBehavior LostFocusCommand="{Binding FinalizeNameCommand}" />
                                </collections:ArrayList>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGridTextColumn.EditingElementStyle>

I do not understand while a binding error is indicated when the binding actually works.

@miloush
Copy link
Contributor

miloush commented May 16, 2024

That is what the CellEditEnding is for, it will get called when the cell is about to finish editing, and you get an opportunity to either cancel and stay in editing mode, or, in your case, save to the db.

I do not understand while a binding error is indicated when the binding actually works.

I will blame the XAML Binding Failures tool window for this. You need to understand that this is not a state of the app. This is binding events collected throughout the debugging session. The binding error happens during XAML loading when the element is created and styled and before it is added to the visual tree where it gets the DataContext. Once you add it to the tree, there isn't any error, but the tool window does not know that. It just keeps adding new entries as errors are logged every time the databinding is evaluated. There is no "you remember that error I told you before? It's gone" event. I prefer checking for databinding errors in the Output window because it reflects the "this happened at this point in time" reality.

@heckl
Copy link
Author

heckl commented May 17, 2024

Dear @miloush

Thank you for the explanation. I understand that the error was temporary and it is not cleared automatically.

I created and updated version.

CellEditEnding event would work fine. Previously I used the LostFocus event and there was no problem with it. But I try to avoid using event and code behind. In the updated version you can see this:

            <local:DataGridTextColumnEx Width="300"
                                      Binding="{Binding TempName, TargetNullValue='', ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
                                      FinalizeCommand="{Binding FinalizeNameCommand}"
                                      Header="Name">
                <DataGridTextColumn.EditingElementStyle>
                    <Style TargetType="{x:Type TextBox}">
                        <Setter Property="local:BehaviorInStyleAttacher.Behaviors">
                            <Setter.Value>
                                <collections:ArrayList>
                                    <local:TextBoxLostFocusBehavior LostFocusCommand="{Binding FinalizeCommand, RelativeSource={RelativeSource AncestorType={x:Type local:DataGridTextColumnEx}}}" />
                                </collections:ArrayList>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGridTextColumn.EditingElementStyle>
            </local:DataGridTextColumnEx>

DataGridTextColumnEx only contains an additional dependency property, the FinalizeCommand. I introduced and additional level on indirection (LostFocusCommand refers to the FinalizeCommand, and it refers to the FinalizeNameCommand), so that I can use the same style at other similar columns. Now binding error is indicated at both command, and now the binding does not work. Can you tell me why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Investigate Requires further investigation by the WPF team.
Projects
None yet
Development

No branches or pull requests

3 participants