在ComboBox中输入垃圾时将SelectedValue设置为0

时间:2016-06-01 13:14:42

标签: c# wpf combobox

我有一个简单的ComboBox,如下所示:

<ComboBox ItemsSource="{DynamicResource ItemsCompColl}" 
          TextSearch.TextPath="ItemName"
          SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged, 
                                  ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
          Grid.IsSharedSizeScope="True">

    ........................

</ComboBox>

效果很好。现在,我正在使用在SelectedValue中绑定的ItemId属性来检查用户是否从comboBox中选择了适当的项目。

的问题:

当用户从ComboBox中选择一个值时,ItemId属性将设置为ComboBox中所选项的Id。之后如果用户进入下一个Control并返回ComboBox并向ComboBox输入一些垃圾值,则ComboBox的ItemId不会改变,我的意思是它没有重置为&#34; 0&#34;。因此,我的验证失败,用户成功输入垃圾值。

2 个答案:

答案 0 :(得分:2)

好的,所以当SelectedValue的可编辑TextBox中存在任何验证错误时,您希望将ComboBox设置为0。您需要检查Text的验证结果,然后在验证失败时将SelectedValue重置为0。

以下是您的工作示例:

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <ComboBox ItemsSource="{Binding ComboboxItems}" 
                      IsEditable="True" DisplayMemberPath="ItemName"
                      Text="{Binding SelectedName, ValidatesOnDataErrors=True}"
                      SelectedValue="{Binding SelectedID, UpdateSourceTrigger=PropertyChanged}" 
                      SelectedValuePath="ItemId"
                      Grid.IsSharedSizeScope="True">                
            </ComboBox>
            <TextBox Text="{Binding SelectedID,UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
    </Grid>
</Window> 

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;    
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        MyViewModel mvm;
        public MainWindow()
        {
            InitializeComponent();
            mvm = new MyViewModel()
            {
                ComboboxItems = new ObservableCollection<ComboItem>() 
                { 
                    new ComboItem{ItemName="item1",ItemId=1},
                    new ComboItem{ItemName="item2",ItemId=2},
                    new ComboItem{ItemName="item3",ItemId=3}
                },
            };
            this.DataContext = mvm;
        }
    }
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected void RaisePropertyChanged(String propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    }


    public class ComboItem : ObservableObject
    {
        private string _itemname;
        private int _itemid;
        public string ItemName
        {
            get
            {
                return _itemname;
            }
            set
            {
                _itemname = value;
                RaisePropertyChanged("ItemName");
            }
        }

        public int ItemId
        {
            get { return _itemid; }
            set
            {
                _itemid = value;
                RaisePropertyChanged("ItemId");
            }
        }
    }

    public class MyViewModel : ObservableObject, IDataErrorInfo
    {
        private int _selectedid;
        private string _selectedname;
        public ObservableCollection<ComboItem> ComboboxItems
        {
            get;
            set;
        }


        public int SelectedID
        {
            get { return _selectedid; }
            set
            {
                if (_selectedid != value)
                {
                    _selectedid = value;
                    RaisePropertyChanged("SelectedID");

                }
            }
        }
        public string SelectedName
        {
            get { return _selectedname; }
            set
            {
                if (_selectedname != value)
                {
                    _selectedname = value;
                    RaisePropertyChanged("SelectedName");

                }
            }
        }   

        public string Error
        {
            get { return this[SelectedName]; }
        }

        public string this[string columnName]
        {
            get {

                switch (columnName)
                {
                    case "SelectedName":
                        {
                            if (SelectedName!=null && ComboboxItems.Count(x => x.ItemName == SelectedName) == 0)
                            {
                                //reset selected value to 0
                                this.SelectedID = 0;
                                return "Invalid selection";

                            }
                            break;
                        }
                }
                return null;
            }
        }
    }
}

结果:

当用户输入有效文本(例如item1)时,下方Textbox会显示ItemId的正确SelectedValue,当用户输入无效文字时,所选值将重置为0

result

PS:当在ComboBox中输入垃圾时,它将始终显示验证错误指示符(如上所示的红色边框),如果您将SelectedItem数据绑定到属性,则它将为null 。因此,如果出现错误,您不应该关心SelectedValue,这就是为什么我说我无法在评论中重现错误。

答案 1 :(得分:2)

你真的让我知道这一点,我从未意识到存在这个问题。我找到了一个解决方案,它可以帮助您清除焦点上的组合。可能有更好的方法,但我无法思考。也许有人在那里有另一个解决方案。

首先,在您的proyect中添加对Windows.System.Interactivity的引用,并将其添加到您的XAML中:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

然后,将此代码添加到组合框:

<ComboBox ItemsSource="{DynamicResource ItemsCompColl}" 
      TextSearch.TextPath="ItemName" x:Name="cbItems"
      SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged, 
                              ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
      Grid.IsSharedSizeScope="True">
      <i:Interaction.Triggers>
         <i:EventTrigger EventName="GotMouseCapture">
             <i:InvokeCommandAction Command="{Binding ClearCombo}" 
               CommandParameter="{Binding ElementName=cbItems}"/>
         </i:EventTrigger>
      </i:Interaction.Triggers>
</ComboBox>

最后,让我们在ouw View Model:

中创建命令
RelayCommand<System.Windows.Controls.ComboBox> _clearCombo;
public ICommand ClearCombo
{
    get
    {
        if (_clearCombo == null)
        {
            _clearCombo = new RelayCommand<System.Windows.Controls.ComboBox>(this.ClearComboCommandExecuted,
            param => this.ClearComboCommandCanExecute());

        }
        return _clearCombo;
    }
}

private bool ClearComboCommandCanExecute()
{
    return true;
}

private void ClearComboCommandExecuted(System.Windows.Controls.ComboBox cb)
{
    cb.Text = "";
}

希望这有助于解决您的问题。

修改

好的,在@XAMlMAX评论之后,我认为他是对的,这可以很容易地在Code Behind中完成,并且可能在MVVM模式中更好。只需在组合框中添加一个事件处理程序即可捕获GotMouseCapture

<ComboBox ItemsSource="{DynamicResource ItemsCompColl}" 
      TextSearch.TextPath="ItemName" x:Name="cbItems"
      SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged, 
                              ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
      Grid.IsSharedSizeScope="True" 
      GotMouseCapture="cbItems_GotMouseCapture" >

然后在View的代码背后:

private void cbItems_GotMouseCapture(object sender, MouseEventArgs e)
{
   ((ComboBox)sender).Text = "";
}

修改2

嗯,最后一个难以理解的想法来解决它。我根本不喜欢它,但也许它可以解决你的问题。

首先,您必须订阅TextBoxBase.TextChanged事件:

<ComboBox ItemsSource="{DynamicResource ItemsCompColl}" 
      TextSearch.TextPath="ItemName" x:Name="cbItems"
      SelectedValue="{Binding ItemId, UpdateSourceTrigger=PropertyChanged, 
                              ValidatesOnDataErrors=True}" SelectedValuePath="ItemId"
      Grid.IsSharedSizeScope="True" 
      TextBoxBase.TextChanged="cbItems_TextChanged" >

然后在代码后面添加此代码:

private void cbItems_TextChanged(object sender, TextChangedEventArgs e)
{
    string text = ((ComboBox)sender).Text;
   ((YourViewModel)this.DataContext).ItemId= text;
}

这样,您确保ComboBox更改其文字时,您会收到相关通知。这是非常可怕的代码,但我已经没有想法......