在应用程序启动时附加 属性 回调事件 return 控件 属性 绑定为空
On app startup Attached Property Callback Event return null for a Control property binding
故事与问题:
我创建 IsActivated
属性 作为附加 属性 以便能够将其添加到我想要的 XAML 页面中的任何控件。然后,当任何控件将其设置为 true
时,我希望触发 PropertyChangedCallback
事件并检查该控件的特定 Property
的绑定。该应用程序 运行 没有任何问题,所有将 IsActivated
属性 设置为 true
的控件都将触发该事件并且绑定对象在 UI 中正确显示值也是。
但问题是,当PropertyChangedcallback
事件执行时,GetBinding()
或GetBindingExpression()
方法将returnnull
那些 Properties
.
的值
(现在我只检查 TextBox
及其 TextProperty
)
背景和我已经尝试过的东西
我在生产应用程序中使用了相同的技术,但是当我尝试简化它向朋友演示时,它不起作用!
我发现,如果我 Set/Change IsActivated
属性 对于 TextBox
在 MainWindow.OnLoaded()
事件,那么在它的回调事件中它会正确 return TextBox.TextProperty
的绑定信息:
private void OnLoaded(object sender, RoutedEventArgs e)
{
MyTextBox.SetValue(ChangeBehavior.IsActivatedProperty, false);
}
但我没有在我的真实应用程序中这样做,我不喜欢这个解决方案!
源代码
这是 GitHub 完整的示例代码。
但这是我的附件属性 class:
public static class ChangeBehavior
{
public static readonly DependencyProperty IsActivatedProperty;
static ChangeBehavior()
{
IsActivatedProperty = DependencyProperty.RegisterAttached
(
"IsActivated",
typeof(bool),
typeof(ChangeBehavior),
new PropertyMetadata(false, OnIsActivatedPropertyChangedCallback)
);
}
public static bool GetIsActivated(DependencyObject obj)
=> (bool)obj.GetValue(IsActivatedProperty);
public static void SetIsActivated(DependencyObject obj, bool value)
=> obj.SetValue(IsActivatedProperty, value);
private static void OnIsActivatedPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox)) return;
// ===> Here I get null value
var binding = BindingOperations.GetBinding(d, TextBox.TextProperty);
if (binding == null) return;
if ((bool)e.NewValue)
{
var sourceObject = binding.Path.Path;
//Doing some stuff...
}
else
{
//Doing some stuff...
}
}
}
这是我的 UI XAML:
<Grid>
<TextBox Name="MyTextBox"
Text="{Binding Model.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
local:ChangeBehavior.IsActivated="True"/>
</Grid>
主窗口代码隐藏:
public partial class MainWindow : Window
{
private readonly ViewModel _viewModel;
public MainWindow(ViewModel viewModel)
{
InitializeComponent();
_viewModel = viewModel;
DataContext = _viewModel;
}
}
ViewModel class:(省略 Notify属性... 使其更短的部分)
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
public ViewModel()
{
Model = new Model {Name = "Model One"};
}
public Model Model
{
get => _model;
set
{
_model = value;
OnPropertyChanged();
}
}
public event PropertyChangedEvent...
}
型号class:(省略通知属性...部分以使其更短)
public class Model : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEvent...
}
}
我的代码有什么问题!
我希望当 InitializeComponent();
从我的 MainWindow
构造函数执行时(就像我的真实应用程序一样)在 XAML 中将 local:ChangeBehavior.IsActivated
设置为 true
的任何控件,我在 OnIsActivatedPropertyChangedCallback
方法中获得了他们 Properties
的正确绑定信息!
为什么几乎相同的技术(和几乎相同的代码!)在我的实际应用程序中有效,但在这个简单的代码中却不起作用!?
我在这里缺少什么!?
更新:解决我的问题的第二种方法是:
与其直接在元素上使用我的 IsAttached
属性,我应该在 ResourceDictionary 中使用它。 (另一种方式是我标记为答案)
我想看看它在您的实际项目中的使用情况。在这一个中,它是在 Text
上设置绑定之前设置的。永远不要让附加属性依赖于初始化顺序,因为那完全不在你的控制范围内。
我们想做的是:能行就行。如果没有,请在加载控件后执行,除非它已经加载,在这种情况下几乎肯定不会有任何事情要做。可以在此处的 Loaded 处理程序中执行此操作,因为 a) class 的消费者不必查看它,并且 b) 您别无选择。
我们理想情况下希望在 Text
上设置绑定时执行此操作,但没有相关事件,并且在运行时替换绑定相对不常见。
private static void OnIsActivatedPropertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if (!tryGetBinding() && !textBox.IsLoaded)
{
void onLoaded(object sender, RoutedEventArgs e2)
{
tryGetBinding();
textBox.Loaded -= onLoaded;
};
textBox.Loaded += onLoaded;
}
bool tryGetBinding()
{
// ===> Here I get null value
var binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
if (binding == null)
return false;
if (GetIsActivated(textBox))
{
var sourceObject = binding.Path.Path;
//Doing some stuff...
}
else
{
//Doing some stuff...
}
return true;
};
}
}
故事与问题:
我创建 IsActivated
属性 作为附加 属性 以便能够将其添加到我想要的 XAML 页面中的任何控件。然后,当任何控件将其设置为 true
时,我希望触发 PropertyChangedCallback
事件并检查该控件的特定 Property
的绑定。该应用程序 运行 没有任何问题,所有将 IsActivated
属性 设置为 true
的控件都将触发该事件并且绑定对象在 UI 中正确显示值也是。
但问题是,当PropertyChangedcallback
事件执行时,GetBinding()
或GetBindingExpression()
方法将returnnull
那些 Properties
.
(现在我只检查 TextBox
及其 TextProperty
)
背景和我已经尝试过的东西
我在生产应用程序中使用了相同的技术,但是当我尝试简化它向朋友演示时,它不起作用!
我发现,如果我 Set/Change IsActivated
属性 对于 TextBox
在 MainWindow.OnLoaded()
事件,那么在它的回调事件中它会正确 return TextBox.TextProperty
的绑定信息:
private void OnLoaded(object sender, RoutedEventArgs e)
{
MyTextBox.SetValue(ChangeBehavior.IsActivatedProperty, false);
}
但我没有在我的真实应用程序中这样做,我不喜欢这个解决方案!
源代码
这是 GitHub 完整的示例代码。
但这是我的附件属性 class:
public static class ChangeBehavior
{
public static readonly DependencyProperty IsActivatedProperty;
static ChangeBehavior()
{
IsActivatedProperty = DependencyProperty.RegisterAttached
(
"IsActivated",
typeof(bool),
typeof(ChangeBehavior),
new PropertyMetadata(false, OnIsActivatedPropertyChangedCallback)
);
}
public static bool GetIsActivated(DependencyObject obj)
=> (bool)obj.GetValue(IsActivatedProperty);
public static void SetIsActivated(DependencyObject obj, bool value)
=> obj.SetValue(IsActivatedProperty, value);
private static void OnIsActivatedPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox)) return;
// ===> Here I get null value
var binding = BindingOperations.GetBinding(d, TextBox.TextProperty);
if (binding == null) return;
if ((bool)e.NewValue)
{
var sourceObject = binding.Path.Path;
//Doing some stuff...
}
else
{
//Doing some stuff...
}
}
}
这是我的 UI XAML:
<Grid>
<TextBox Name="MyTextBox"
Text="{Binding Model.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
local:ChangeBehavior.IsActivated="True"/>
</Grid>
主窗口代码隐藏:
public partial class MainWindow : Window
{
private readonly ViewModel _viewModel;
public MainWindow(ViewModel viewModel)
{
InitializeComponent();
_viewModel = viewModel;
DataContext = _viewModel;
}
}
ViewModel class:(省略 Notify属性... 使其更短的部分)
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
public ViewModel()
{
Model = new Model {Name = "Model One"};
}
public Model Model
{
get => _model;
set
{
_model = value;
OnPropertyChanged();
}
}
public event PropertyChangedEvent...
}
型号class:(省略通知属性...部分以使其更短)
public class Model : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEvent...
}
}
我的代码有什么问题!
我希望当 InitializeComponent();
从我的 MainWindow
构造函数执行时(就像我的真实应用程序一样)在 XAML 中将 local:ChangeBehavior.IsActivated
设置为 true
的任何控件,我在 OnIsActivatedPropertyChangedCallback
方法中获得了他们 Properties
的正确绑定信息!
为什么几乎相同的技术(和几乎相同的代码!)在我的实际应用程序中有效,但在这个简单的代码中却不起作用!?
我在这里缺少什么!?
更新:解决我的问题的第二种方法是:
与其直接在元素上使用我的 IsAttached
属性,我应该在 ResourceDictionary 中使用它。 (另一种方式是我标记为答案)
我想看看它在您的实际项目中的使用情况。在这一个中,它是在 Text
上设置绑定之前设置的。永远不要让附加属性依赖于初始化顺序,因为那完全不在你的控制范围内。
我们想做的是:能行就行。如果没有,请在加载控件后执行,除非它已经加载,在这种情况下几乎肯定不会有任何事情要做。可以在此处的 Loaded 处理程序中执行此操作,因为 a) class 的消费者不必查看它,并且 b) 您别无选择。
我们理想情况下希望在 Text
上设置绑定时执行此操作,但没有相关事件,并且在运行时替换绑定相对不常见。
private static void OnIsActivatedPropertyChangedCallback(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
if (!tryGetBinding() && !textBox.IsLoaded)
{
void onLoaded(object sender, RoutedEventArgs e2)
{
tryGetBinding();
textBox.Loaded -= onLoaded;
};
textBox.Loaded += onLoaded;
}
bool tryGetBinding()
{
// ===> Here I get null value
var binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
if (binding == null)
return false;
if (GetIsActivated(textBox))
{
var sourceObject = binding.Path.Path;
//Doing some stuff...
}
else
{
//Doing some stuff...
}
return true;
};
}
}