当未加载控件时,有没有办法停用绑定?

时间:2019-12-18 17:46:50

标签: c# xaml data-binding

通常,绑定是使用Ca中的可视化表示形式连接C#中的数据的一种非常方便的方法。数据更改时,视觉表示也会自动更新。但是,如果您有成千上万个绑定,并且大多数都位于当前不在屏幕上的页面上,则使所有隐藏的视觉表示保持最新状态似乎很愚蠢且昂贵。但是,如您所见,如果您运行下面的示例,则默认情况下绑定就是这样做的!即使我从屏幕上删除了CountingBlock,每次BreakpointConverter更新时,它仍然继续呼叫我的Counter

对我来说,在卸载时停止更新是更有意义的,然后作为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>

转换器:

class BreakpointConverter : System.Windows.Data.IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
         return value; // does nothing, but you can set a breakpoint here to see if the binding is active.
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

背后的代码

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));
}

1 个答案:

答案 0 :(得分: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);
    }
}

Clone() Method

GetBindingPaths() Method-仅保存BindingExpressions而不是路径。