dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.05k stars 1.17k forks source link

Framework Measure() method is not calculated the size properly While using the Relative source. #9076

Open ghost opened 5 months ago

ghost commented 5 months ago

Description

I added a Label into the Grid in WPF and set its content using RelativeSource Binding. However, during calculation, the size was improperly determined in the Measure(), resulting in the content not being visible in the view. Interestingly, when I switched to using a static resource instead, the issue resolved, and the content displayed properly.

RelativeSource.zip

Reproduction Steps

Run the sample. The content of the label located in the middle is not being updated.

image

Expected behavior

The content should be updated properly when binding with relative source.

Actual behavior

The content is not updated in the view because of improperly calculated the size.

Regression?

No response

Known Workarounds

No response

Impact

No response

Configuration

No response

Other information

No response

MichaeIDietrich commented 5 months ago

OK, it really took me quite some time to understand your problem here. 😅

What we see here is expected. You have to know that InitializeComponent() means that the basic visual tree has been created from the XAML. This also includes the resolution of things like StaticResource and some data bindings ("first chance expressions").

Data binding features like RelativeSource cannot be evaluated as first chance expressions, since they need a fully constructed visual tree, which is not present at the time first chance expressions are evaluated. There is at least one layout pass needed to make RelativeSource to be evaluated, so the Content of Label1 stays empty after InitializeComponent() was invoked, resulting in DesiredSize being small for Label1.

I'm not really sure what you are trying to achieve in your code but usually you should not invoke Measure() manually instead you should rely on events or priority based dispatching.

WPF uses the concept of a Dispatcher to schedule actions with a specific priority. So what you probably want to do instead is to have your measuring to take place after all your data bindings have been evaluated, which would just be the case if you would not manually measure here. :)

So you could for example wrap your manual measuring into Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.DataBind, new Action(() => { /* your code */ })); to ensure all data bindings have been evaluated so far before manually measuring or maybe just hook up an event handler to Window.LayoutUpdated to have DesiredSize already being updated for all the elements in your window without manually invoking Measure().