1

I have implemented the prism MVVM pattern in my desktop WPF non-silverlight application. During the screen loading, I am populating a listbox with a collection of values. This listbox is bound to the ViewModel ( which is my DataContext ) and each item is rendered using a DataTemplate. This is working fine. I am then trying to create a textbox that will allow the user to filter down which records are displayed when they type into it. My natural thought was to go after the KeyUp or TextChanged events, but it turns out that this is more difficult than just binding to the event if trying to maintain the use of ICommand as recommended. I could not find an easy way around this problem and did not want to spend anymore time figuring it out as I have a deadline fast approaching. With that in mind, I attempted the following solution:

XAML:

<TextBox Name="ChannelSearch" Text="{Binding Path=ChannelFilter, Mode=TwoWay}"  TextChanged="ChannelSearch_TextChanged" />

Code-Behind:

private void ChannelSearch_TextChanged(object sender, TextChangedEventArgs e) {
        var vm = ((IExpressionEditorViewViewModel)ViewModel);
        if (vm.FilterChannels.CanExecute())
            vm.FilterChannels.Execute();
    }

ViewModel:

 //In the constructor:
 FilterChannels = new DelegateCommand(doFilterChannels, doCanFilterChannels);

 //Class properties:
 public string ChannelFilter { get; set; }

 //Methods used by DelegateCommand:
 private void doFilterChannels(){
        if (string.IsNullOrWhiteSpace(ChannelFilter))
            this.IdFileRows = _allIdFileRows;
        else{
            var lower = ChannelFilter.Trim().ToLower();

            this.IdFileRows = _allIdFileRows.Where(
                r => (DisplayChannel && r.A_RowChannelNum.ToLower().Contains(lower))
                    || (DisplayMnemonic && r.B_Mnemonic.ToLower().Contains(lower))
                    || (DisplayDescription && r.F_Description.ToLower().Contains(lower))
                ).ToList();
        }
    }

 private bool doCanFilterChannels() {

        return true;
    }

So with all of that in place, I started debugging. The results did not differ between KeyUp and TextChanged. When "doFilterChannels" is hit, the ChannelFilter text is null everytime. What starts to boggle my mind is that if I hit a save button I made on the screen which hits a DelegateCommmand directly like so:

<Button Content="Save" Command="{Binding Path=SaveExpression}"></Button>

SaveExpression = new DelegateCommand(doSaveExpression, doCanSaveExpression);

      private void doSaveExpression() {
        Result result = new Result();
        try {

            result.Success = true;
        }
        catch (Exception e) {
            result.ResultMessages.Add(new ResultMessage { ErrorCode = "500", Message = e.ToString() });
        }

        result.ThrowErrors();
    }
    private bool doCanSaveExpression() {
        return true; //TODO validation
    }

When I break inside the doSaveExpression method and check ChannelFilter, the value is there and accurate. Why is this happening here and not when the KeyUp/TextChanged events are fired? Is there a better way to handle these situations?

4

1 回答 1

3

UpdateSourceTrigger属性的默认值是LostFocusa TextBox,这就是为什么在您将焦点从文本框移到另一个控件(在您的情况下,点击按钮)之前不会更新您的 ViewModel 属性的原因。在绑定中设置UpdateSourceTriggerto的值。PropertyChanged

例如:

<TextBox Name="ChannelSearch" 
         Text="{Binding Path=ChannelFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  
         TextChanged="ChannelSearch_TextChanged" />

请参阅此 MSDN文章以了解有关 UpdateSourceTrigger 属性的更多信息。

对于您最初的问题,您可以从viewModel 上doFilterChannels的属性设置器调用方法(当然,不会阻塞 UI 线程)。ChannelFilter

于 2013-10-03T15:59:30.843 回答