【问题标题】:Why can't I reflect a list box choice in a text box in WPF?为什么我不能在 WPF 的文本框中反映列表框选择?
【发布时间】:2016-02-18 21:43:52
【问题描述】:

我是 WPF 新手,我在现有设置中遇到了一些问题,无法让列表框选定项出现在文本框中。

这里的图片代表了问题。我在文本框中输入了“12 HOUR”,然后将列表框过滤到字符串中任何位置带有“12 HOUR”的项目。但是当我点击列表框中的“12 Hour Nasal”时,我现在想在文本框中反映该选择:

http://i.imgur.com/ZCYAolT.png

这是包含列表框和文本框的用户控件的 XAML:

<UserControl x:Class="SCM_AllergyRecModule.SearchAndSelectView"
         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" 
         mc:Ignorable="d">
<StackPanel Width="300">
    <TextBox x:Name="Filter" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>
    <ListBox Width ="300" Height="50" x:Name="ListBoxControl"
           ItemsSource="{Binding Path=Allergens}"            
           SelectedItem="{Binding Path=SelectedAllergen}">         
    </ListBox>
</StackPanel>

这里是 ViewModel:

namespace SCM_AllergyRecModule
{
public class SearchAndSelectViewModel
{
    private ICollectionView allergens;
    private string selectedAllergen;
    private string filter = "";      

    public string Filter
    {
        get
        {
            return this.filter.ToUpperInvariant();
        }
        set
        {
            if (this.filter != value)
            {
                this.filter = value;
                this.Allergens.Refresh();
            }
        }
    }

    private bool ContainsFilter(object item)
    {
        var product = item as string;
        if (product == null)
        {
            return false;
        }

        if (string.IsNullOrEmpty(this.Filter))
        {
            return true;
        }

        if (product.ToUpperInvariant().Contains(this.Filter))
        {
            return true;
        }

        return false;
    }

    public SearchAndSelectViewModel()
    {
        var cvs = new CollectionViewSource();
        cvs.Source = MainWindow.scmAllergens;
        this.allergens = cvs.View;
        this.allergens.Filter = ContainsFilter;
    }

    public ICollectionView Allergens
    {
        get
        {
            return this.allergens;
        }
    }

    public string SelectedAllergen
    {
        get
        {
            return this.selectedAllergen;
        }
        set
        {
            if (this.selectedAllergen != value)
            {
                this.selectedAllergen = value;
            }
        }
    }
}
}

更新 1

我将 INotifyPropertyChanged 接口添加到我的类中,并在 setter 中的 SelectedAllergen 上引发它。我添加了一个名为 SearchAndSelectViewModel_PropertyChanged 的​​事件处理程序来处理 SelectedAllergen 属性更改并将其设置在构造函数中。

现在,当我单击列表框中的一个项目时,我确实看到它将过滤器设置为 SelectedItem,而列表过滤器设置为该项目,因此没有其他内容显示。但是,文本框文本仍然没有改变?请参阅下面的屏幕截图。这是在我在文本框中输入“PEAN”之后,然后列表框过滤为两个选项,然后我选择了“PEANUTS (FOOD)”,然后重新过滤列表框以仅显示该选项但未设置文本框到“花生(食物)”:

http://imgur.com/dNxuVI5

更新的视图模型

public class SearchAndSelectViewModel : INotifyPropertyChanged
{
    private ICollectionView allergens;
    private string selectedAllergen;
    private string filter;

    public string Filter
    {
        get
        {
            return this.filter.ToUpperInvariant();
        }
        set
        {
            if (this.filter != value)
            {
                this.filter = value;
                this.Allergens.Refresh();
            }
        }
    }

    private bool ContainsFilter(object item)
    {
        var product = item as string;
        if (product == null)
        {
            return false;
        }


        if (string.IsNullOrEmpty(this.Filter))
        {
            return true;
        }


        if (product.ToUpperInvariant().Contains(this.Filter))
        {
            return true;
        }

        return false;
    }

    private void SearchAndSelectViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "SelectedAllergen":
                this.Filter = this.SelectedAllergen;
                break;
        }
    }

    public SearchAndSelectViewModel()
    {
        filter = "";
        var cvs = new CollectionViewSource();
        cvs.Source = MainWindow.scmAllergens;
        this.allergens = cvs.View;
        this.allergens.Filter = ContainsFilter;
        this.PropertyChanged += SearchAndSelectViewModel_PropertyChanged;
    }

    public ICollectionView Allergens
    {
        get
        {
            return this.allergens;
        }
    }

    public string SelectedAllergen
    {
        get
        {
            return this.selectedAllergen;
        }
        set
        {
            if (this.selectedAllergen != value && value != null)
            {
                this.selectedAllergen = value;
                OnPropertyChanged("SelectedAllergen");
            }
        }
    }

    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【问题讨论】:

  • SelectedAllergen 被设置时,为什么不也设置你的Filter? (但可能需要更好的机制,因为您在 setter 中有一些业务逻辑)。看起来您也缺少INotifyPropertyChanged。视图没有任何东西可以用来监听属性值的变化。
  • 或者你可以简单地监听 listbox 上的 selectionchanged 事件并设置过滤器值´

标签: c# wpf xaml mvvm user-controls


【解决方案1】:

您需要实现 INotifyPropertyChanged 接口,并且可以在属性设置器中调用它。由于您还将 TextBox 绑定到 Filter 属性,因此您需要在 SelectedAllergen 更改时设置 Filter 属性。

INotifyPropertyChanged 示例:

public class MyViewModel : INotifyPropertyChanged
{
    //...

    private int myProperty = 0;
    public int MyProperty 
    {
        get { return myProperty; }
        set
        {
            myProperty = value;

            // Raise the property changed notification
            OnPropertyChanged("MyProperty");
        }
    }


    // INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if(handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

【讨论】:

  • 谢谢。我添加了 INotifyPropertyChanged 接口,并在我的 SelectedAllergen 设置器中引发了该事件。那么如何在知道 SelectedAllergen OnPropertyChanged 已引发的情况下设置 Filter 属性?
  • 视图模型中的任何属性如果在 XAML 中用作绑定,则应引发属性更改。您可以订阅 SelectionChanged 事件并设置 Filter 属性或在 SelectedAllergen setter 中设置 Filter 属性(顺便说一句,我不喜欢从另一个属性的 setter 设置属性)。
  • 请看我的更新。我听从了您的建议,但大多数情况下仍然没有点击。
  • 如果要将更改反映到 TextBox,请在 Filter 属性设置器中调用 OnPropertyChanged("Filter")。
  • 就是这样!谢谢你。你能快速解释一下为什么它会起作用吗?
猜你喜欢
  • 2014-08-29
  • 1970-01-01
  • 2017-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 2022-11-18
  • 1970-01-01
相关资源
最近更新 更多