多次单击按钮后 ICommand "freezes"的 WPF 数据绑定



这是我第一次尝试数据限制一个iCommand。我有一个数字LED控件,我想像一个按钮一样行动,因此我更改了按钮控件的数据板,看起来像LED:

led.xaml

<UserControl x:Class="LedControlDatabindingTest.LED"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             x:Name="root"
             mc:Ignorable="d" 
             Height="Auto" Width="Auto">
    <Grid DataContext="{Binding ElementName=root}">
        <StackPanel Orientation="{Binding LEDOrientation, FallbackValue=Vertical}">
        <!-- LED portion -->
            <Button BorderBrush="Transparent" Background="Transparent" Click="Button_Click">
                <Button.ContentTemplate>
                    <DataTemplate>
                        <Grid>
                            <Ellipse Grid.Column="0" Margin="3" Height="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
                                     Width="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
                                     Fill="{Binding ElementName=root, Path=LEDColor, FallbackValue=Green}"
                                     StrokeThickness="2" Stroke="DarkGray" HorizontalAlignment="Center" />
                            <Ellipse Grid.Column="0" Margin="3" Height="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}"
                                     Width="{Binding ElementName=root, Path=LEDSize, FallbackValue=16}" HorizontalAlignment="Center">
                                <Ellipse.Fill>
                                    <RadialGradientBrush GradientOrigin="0.5,1.0">
                                        <RadialGradientBrush.RelativeTransform>
                                            <TransformGroup>
                                                <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
                                                <TranslateTransform X="0.02" Y="0.3"/>
                                            </TransformGroup>
                                        </RadialGradientBrush.RelativeTransform>
                                        <GradientStop Offset="1" Color="#00000000"/>
                                        <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
                                    </RadialGradientBrush>
                                </Ellipse.Fill>
                            </Ellipse>
                        </Grid>
                    </DataTemplate>
                </Button.ContentTemplate>
            </Button>
            <!-- label -->
            <TextBlock Grid.Column="1" Margin="3" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding LEDLabel, FallbackValue=0}" />
        </StackPanel>
    </Grid>
</UserControl>

我希望主机应用程序能够数据限于LED的大小,颜色和标签等属性。另外,我希望能够绑定到命令处理程序。

led.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LedControlDatabindingTest {
    /// <summary>
    /// Interaction logic for LED.xaml
    /// </summary>
    public partial class LED : UserControl
    {
        public static DependencyProperty LEDColorProperty = DependencyProperty.Register( "LEDColor", typeof(Brush), typeof(LED));
        public Brush LEDColor
        {
            get { return this.GetValue(LEDColorProperty) as Brush; }
            set { 
                this.SetValue( LEDColorProperty, value);
            }
        }
        public static DependencyProperty LEDSizeProperty = DependencyProperty.Register( "LEDSize", typeof(int), typeof(LED));
        public int LEDSize
        {
            get { return (int)GetValue(LEDSizeProperty); }
            set { 
                SetValue( LEDSizeProperty, value);
            }
        }
        public static DependencyProperty LEDLabelProperty = DependencyProperty.Register( "LEDLabel", typeof(string), typeof(LED));
        public string LEDLabel
        {
            get { return (string)GetValue(LEDLabelProperty); }
            set { 
                SetValue( LEDLabelProperty, value);
            }
        }
        public static DependencyProperty LEDOrientationProperty = DependencyProperty.Register( "LEDOrientation", typeof(Orientation), typeof(LED));
        public Orientation LEDOrientation
        {
            get { return (Orientation)GetValue(LEDOrientationProperty); }
            set { 
                SetValue( LEDOrientationProperty, value);
            }
        }
        public static readonly DependencyProperty LEDClickedProperty = DependencyProperty.Register("LEDClicked", typeof(ICommand), typeof(LED), new PropertyMetadata(null));
        public ICommand LEDClicked
        {
            get { return (ICommand)GetValue(LEDClickedProperty); }
            set { SetValue( LEDClickedProperty, value); }
        }
        public LED()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            LEDClicked.Execute( null);
        }
    }
}

我的测试应用程序很简单。

mainwindow.xaml:

<Window x:Class="LedControlDatabindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:LedControlDatabindingTest"
        Title="MainWindow" Height="70" Width="250">
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Digital Inputs:" />
            <ListBox ItemsSource="{Binding AvailableDigitalInputs}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel IsItemsHost="True" Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <local:LED LEDLabel="{Binding Index}" LEDColor="{Binding Color}" LEDSize="12" LEDClicked="{Binding Clicked}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </StackPanel>
</Window>

我的代码范围中有不可能的信息,例如我的应用程序的DataContext,但我认为出于此演示的目的,现在还可以。

mainwindow.xaml.cs:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace LedControlDatabindingTest {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private class DigitalInputData : ViewModelBase
        {
            private Brush _color;
            public Brush Color 
            {
                get { return _color; }
                set {
                    _color = value;
                    RaisePropertyChanged();
                }
            }
            public int Index { get; set; }
            public ICommand Clicked { get; set; }
            private bool _state;
            public DigitalInputData( int index, Brush on_color)
            {
                Index = index;
                Color = Brushes.LightGray;
                Clicked = new RelayCommand( () => {
                    // get current state of this digital input and then toggle it
                    _state = !_state;
                    // read back and update here until I get threaded updates implemented                    
                    Color = _state ? on_color : Brushes.LightGray;
                });
            }
        }
        private List<DigitalInputData> _inputs = new List<DigitalInputData>();
        public ICollectionView AvailableDigitalInputs { get; set; }
        public MainWindow()
        {
            InitializeComponent();
            // For this example only, set DataContext in this way
            DataContext = this;
            for( int i=0; i<4; i++) {
                _inputs.Add( new DigitalInputData( i, Brushes.Green));
            }
            AvailableDigitalInputs = CollectionViewSource.GetDefaultView( _inputs);
        }
    }
}

当我运行此应用程序时,所有内容都会正确呈现,并根据我的数据属性属性。点击处理程序也可以工作,并切换LED的状态。

但是,当我在某个时候单击LED按钮多次(也许是在20次点击左右)时,它就停止调用我的数据库ICommand。为什么?

我很幸运,尽管我不了解技术推理,但我找到了解决"冻结"问题的解决方案。

在我的DigitalInputData构造函数中,我使用lambda函数为中继器创建了处理程序,而不是传递对处理程序的引用。一旦我切换到将处理程序传递到中继命令构造函数,它就可以了。

最新更新