多次按下Button后,ICommand的WPF数据绑定“冻结”

时间:2017-09-22 13:42:57

标签: c# wpf xaml data-binding

这是我第一次尝试数据绑定ICommand。我有一个数字LED控制器,我想像一个按钮,所以我改变了DataTemplate的Button控件看起来像一个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>

在我的代码隐藏中有这样的no-nos,就像我的应用程序的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。为什么?

1 个答案:

答案 0 :(得分:0)

我很幸运并想出了解决“冻结”问题的方法,虽然我不理解技术推理。

在我的DigitalInputData构造函数中,我使用lambda函数为RelayCommand创建了处理程序,而不是将引用传递给处理程序。一旦我切换到将处理程序传递给RelayCommand构造函数,它就运行得很好。