C# 当控件未加载时,是否有方法停用绑定?

C# 当控件未加载时,是否有方法停用绑定?,c#,xaml,data-binding,C#,Xaml,Data Binding,一般来说,绑定是连接C#中的数据和Xaml中的可视化表示的一种非常方便的方法。当数据更改时,视觉表示也会自动更新。但是,如果您有数千个绑定,并且大多数绑定都位于当前不在屏幕上的页面上,那么将所有隐藏的视觉表示保持最新似乎既愚蠢又昂贵。但正如您所看到的,如果您运行下面的示例,这正是绑定在默认情况下所做的!即使我从屏幕上删除CountingBlock,每次计数器更新时,它都会继续调用我的断点转换器 对我来说,在卸载时停止更新更有意义,然后作为LoadedEvent的一部分,重新激活我的所有绑定,同时

一般来说,绑定是连接C#中的数据和Xaml中的可视化表示的一种非常方便的方法。当数据更改时,视觉表示也会自动更新。但是,如果您有数千个绑定,并且大多数绑定都位于当前不在屏幕上的页面上,那么将所有隐藏的视觉表示保持最新似乎既愚蠢又昂贵。但正如您所看到的,如果您运行下面的示例,这正是绑定在默认情况下所做的!即使我从屏幕上删除
CountingBlock
,每次
计数器更新时,它都会继续调用我的
断点转换器

对我来说,在卸载时停止更新更有意义,然后作为LoadedEvent的一部分,重新激活我的所有绑定,同时检查源已更新到什么有没有办法做到这一点?

谢谢

Xaml:

<Window x:Class="DeactivateBindings.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="450" Width="800">
  <UniformGrid>
    <ContentControl Name="Container"/>
    <Button Content="Show or Hide" Click="ShowOrHide_Click"/>
  </UniformGrid>
</Window>
背后的代码:

public partial class MainWindow : Window
{
    bool _isCounterVisible;
    Label CountingBlock = new Label { FontSize = 25 };

    public MainWindow()
    {
        InitializeComponent();
        ContingBlock.SetBinding(Label.ContentProperty, new Binding
        {
            Source = this,
            Path = new PropertyPath(nameof(Counter)),
            // this Converter is the key component that tells us the binding is still active, because we can breakpoint it.
            Converter = new BreakpointConverter(), 
        });

        StartCounting();
    }

    private async void StartCounting()
    {
        while(true)
        {
             Counter++;
             await Task.Delay(500);
        }
    }

    private void ShowOrHide_Click(object sender, RoutedEventArgs e)
    {
        _isCounterVisible = !_isCounterVisible;

        Countainer.Content = _isCounterVisible ? CountingBlock : null;
    }

    public int Counter
    {
         get { return (int)GetValue(CounterProperty); }
         set {SetValue(CounterProperty, value); }
    }
    public static readonly DependencyProperty CounterProperty = 
        DependencyProperty.Register(nameof(Counter), typeof(int), typeof(MainWindow), new PropertyMetadata(0));
}

一种选择是在离开页面时将所有绑定更改为显式,然后稍后再将其更改回显式
Explicit
意味着绑定只会在您告诉它时更新,而不是在备份属性更改时更新。重新加载UIElement后,可以再次重新激活所有绑定

// this will allow us to keep track of the real UpdateSourceTriggers for our bindings
private Dictionary<BindingExpression, UpdateSourceTrigger> _deactivatedBindings = new Dictionary<BindingExpression, UpdateSourceTrigger>();

public static void OnUnloaded(object sender, RoutedEventArgs e)
{
    foreach(BindingExpreesion expression in GetBindingPaths(sender as DependencyObject))
        expression.DeactivateBindings();
}

public static void OnLoaded(object sender, RoutedEventArgs e)
{
    foreach(BindingExpreesion expression in GetBindingPaths(sender as DependencyObject))
        expression.ReactivateBindings();
}

public static asynce Task DeactivateBindings(this BindingExpression oldExpression)
{
    // you can't modify bindings once they've been used, so we'll copy the old one, modify it, and use that one instead
    Binding duplicateBinding = oldExpression.ParentBinding.Clone();

    // this means that the binding won't update when the property changes, just when we tell it to.
    duplicateBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;

    // here we save the 'real' value - the one we use when the element is loaded.
    _deactivatedBindings[duplicateBinding] = oldExpression.ParentBinding.UpdateSourceTrigger;

    BindingOperations.SetBinding(oldExpression.Target, oldExpression.TargetProperty, duplicateBinding);
}

public static async Task ReactivateBindings(this BindingExpression oldExpression)
{
    if(_deactivatedBindings.TryGetValue(oldExpression.ParentBinding, out UpdateSourceTrigger realTrigger))
    {
         _deactivatedBindings.Remove(oldExpression.ParentBinding);
         Binding duplicateBinding = oldExpression.ParentBinding.Clone();
         duplicateBinding.UpdateSourceTrigger = realTrigger;
         // This has the added advantage of refreshing the value so it's up to date. :)
         BindingOperations.SetBinding(oldExpression.Target, oldExpression.TargetProperty, duplicateBinding);
    }
}
//这将允许我们跟踪绑定的实际更新资源记录器
私有字典_deactivatedBindings=新字典();
未加载的公共静态无效(对象发送方,路由目标)
{
foreach(GetBindingPath中的BindingExpression表达式(发送方作为DependencyObject))
表达式。停用索引();
}
已加载公共静态无效(对象发送器,路由目标e)
{
foreach(GetBindingPath中的BindingExpression表达式(发送方作为DependencyObject))
表达式。重新激活索引();
}
公共静态异步任务停用索引(此BindingExpression-oldExpression)
{
//一旦绑定被使用,您就不能修改它们,因此我们将复制旧绑定,修改它,然后使用该绑定
Binding duplicateBinding=oldExpression.ParentBinding.Clone();
//这意味着绑定不会在属性更改时更新,而只是在我们告诉它时更新。
duplicateBinding.UpdateSourceTrigger=UpdateSourceTrigger.Explicit;
//在这里,我们保存“真实”值—加载元素时使用的值。
_deactivatedBindings[duplicateBinding]=oldExpression.ParentBinding.UpdateSourceTrigger;
BindingOperations.SetBinding(oldExpression.Target、oldExpression.TargetProperty、duplicateBinding);
}
公共静态异步任务重新激活Bindings(此BindingExpression-oldExpression)
{
if(_deactivatedBindings.TryGetValue(oldExpression.ParentBinding,out UpdateSourceTrigger realTrigger))
{
_deactivatedBindings.Remove(oldExpression.ParentBinding);
Binding duplicateBinding=oldExpression.ParentBinding.Clone();
duplicateBinding.UpdateSourceTrigger=realTrigger;
//这还具有刷新值的附加优势,因此它是最新的。:)
BindingOperations.SetBinding(oldExpression.Target、oldExpression.TargetProperty、duplicateBinding);
}
}

-只需保存BindingExpressions而不是路径

// this will allow us to keep track of the real UpdateSourceTriggers for our bindings
private Dictionary<BindingExpression, UpdateSourceTrigger> _deactivatedBindings = new Dictionary<BindingExpression, UpdateSourceTrigger>();

public static void OnUnloaded(object sender, RoutedEventArgs e)
{
    foreach(BindingExpreesion expression in GetBindingPaths(sender as DependencyObject))
        expression.DeactivateBindings();
}

public static void OnLoaded(object sender, RoutedEventArgs e)
{
    foreach(BindingExpreesion expression in GetBindingPaths(sender as DependencyObject))
        expression.ReactivateBindings();
}

public static asynce Task DeactivateBindings(this BindingExpression oldExpression)
{
    // you can't modify bindings once they've been used, so we'll copy the old one, modify it, and use that one instead
    Binding duplicateBinding = oldExpression.ParentBinding.Clone();

    // this means that the binding won't update when the property changes, just when we tell it to.
    duplicateBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;

    // here we save the 'real' value - the one we use when the element is loaded.
    _deactivatedBindings[duplicateBinding] = oldExpression.ParentBinding.UpdateSourceTrigger;

    BindingOperations.SetBinding(oldExpression.Target, oldExpression.TargetProperty, duplicateBinding);
}

public static async Task ReactivateBindings(this BindingExpression oldExpression)
{
    if(_deactivatedBindings.TryGetValue(oldExpression.ParentBinding, out UpdateSourceTrigger realTrigger))
    {
         _deactivatedBindings.Remove(oldExpression.ParentBinding);
         Binding duplicateBinding = oldExpression.ParentBinding.Clone();
         duplicateBinding.UpdateSourceTrigger = realTrigger;
         // This has the added advantage of refreshing the value so it's up to date. :)
         BindingOperations.SetBinding(oldExpression.Target, oldExpression.TargetProperty, duplicateBinding);
    }
}