Closed guchen66 closed 2 weeks ago
Please provide a reproduction app (not a code snippet), or this issue will be closed.
Allow me to translate:
I get an error message when I use
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
. What I want to do is make everyTextBox
of the whole page read-only, so I go through the whole visual tree and when I find aTextBox
set it to read-only. Here is a very simple example:<Window x:Class="XamlBehaviorsTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" Height="350" Width="525" > <i:Interaction.Triggers> <i:EventTrigger EventName="Loaded"> <i:InvokeCommandAction Command="{Binding BtnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" /> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <StackPanel> <TextBox Width="200" Text="200" Height="20" Margin="10"/> <Button Width="200" Height="20" Command="{Binding BtnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}"/> </StackPanel> </Grid> </Window>
public class MainWindowViewModel : BindableBase { public ICommand BtnCommand { get; set; } public MainWindowViewModel() { BtnCommand = new DelegateCommand<DependencyObject>(ExecuteBtnCommand); } private void ExecuteBtnCommand(DependencyObject obj) { SetTextBoxesReadOnly(obj,true); } public void SetTextBoxesReadOnly(DependencyObject parent, bool isReadOnly) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is TextBox textBox) { textBox.IsReadOnly = isReadOnly; } else { SetTextBoxesReadOnly(child, isReadOnly); } } } }
Writing it like this works, but when I remove the
CommandParameter
attribute from the<Button Width="200" Height="20" Command="{Binding BtnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}"/>
I get a System.ArgumentNullException: "Value cannot be null. Arg_ParamName_Name".
The error:
CommandParameter
attribute of the Button
is present, the InvokeCommandAction
passes the Window
object to ExecuteBtnCommand
.CommandParameter
attribute of the Button
is removed, it will pass null
to ExecuteBtnCommand
.So in a nutshell: removing the CommandParameter
attribute of a Button
causes an unrelated InvokeCommandAction
to no longer evaluate/recognize a CommandParameter
.
I have created a test project, and tried the following:
ICommand
objects for the Button
and the InvokeCommandAction
- does not change anythingx:Name="ICA"
to the InvokeCommandAction
and a Loaded="Window_Loaded"
event handler to the Window
; add a breakpoint in Window_Loaded
and use the direct window to inspect ICA
:
ICA.CommandParameter
is {XamlBehaviorsTest.MainWindow}
ICA.CommandParameter
is null
.ICA
are identical for both cases.The WPF binding engine might be optimizing or delaying certain bindings until it encounters a scenario where it needs to resolve them. Setting the CommandParameter on the Button could be causing the binding engine to resolve all bindings within that context earlier or more thoroughly, including those in the InvokeCommandAction.
In general, I would recommend avoiding RelativeSource
bindings if possible and just use the ElementName
instead.
@brianlagunas But does using ElementName
fix the issue, or is it related to https://github.com/dotnet/wpf/issues/316 / https://github.com/dotnet/wpf/pull/4217 ?
我在使用xmlns:i="http://schemas.microsoft.com/xaml/behaviors"时候报错, 我想要实现的效果是让整个页面的TextBox都是只读状态,所以我遍历整个视觉树,找到TextBox,设置为只读。 下面是一个很简单的例子