如何在运行时动态加载两个相同的用户控件,而不允许一个控件中的更改影响另一个控件?

时间:2016-02-11 22:17:58

标签: c# wpf mvvm user-controls

我无法在WPF中重复使用用户控件,我希望你能提供帮助。

在运行时,我正在创建此用户控件的新实例以显示在WPF网格中。用户控件由文本框和列表框组成,其中文本框中的用户输入过滤列表框中的结果。比如说这个例子,我在运行时动态地创建了两个相同的用户控件,共享相同的ViewModel。但是,当我运行应用程序时,如果我在第二个用户控件文本框中输入内容,它会反映在用户控件的两个列表框中。我只希望它反映在自己的列表框中。

以下是前端问题的图片。突出显示的输入反映在两个列表框中,但不应该:

enter image description here

以下是用户控件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="150">
    <TextBox x:Name="Filter" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>
    <ListBox Height="50" 
           ItemsSource="{Binding Path=Allergens}"
           IsSynchronizedWithCurrentItem="True"
           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()
    {
        this.allergens = CollectionViewSource.GetDefaultView(MainWindow.scmAllergens);
        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;
            }
        }
    }
}
}

以下是主窗口中运行时用户控件的动态加载(请暂时忽略命名):

SearchAndSelectView ssvAllergenSearch = new SearchAndSelectView();
            ssvAllergenSearch.DataContext = new SearchAndSelectViewModel();
            controlName = "ssvAllergenSearch" + (rowCounter.ToString() + allergenColumn.ToString());
            ssvAllergenSearch.Name = controlName;
            this.RegisterName(controlName, cmbAllscriptsCategory);
            Grid.SetRow(ssvAllergenSearch, rowCounter);
            Grid.SetColumn(ssvAllergenSearch, allergenColumn);
            DynamicGrid.Children.Add(ssvAllergenSearch);

2 个答案:

答案 0 :(得分:1)

我认为这是违规行:

this.allergens = CollectionViewSource.GetDefaultView(MainWindow.scmAllergens);

我假设您只有一个MainWindow实例,因此只有一个scmAllergens实例。通过调用CollectionViewSource.GetDefaultView,您将获得相同的CollectionViewSource次。改为手动创建一个新实例:

var cvs = new CollectionViewSource();
cvs.Source = MainWindow.scmAllergens;
this.allergens = cvs.View;

答案 1 :(得分:1)

如果他们共享相同的视图模型,那就是您的问题。您的控件被DataContext绑定到同一个源,因此目标链接回同一个实例,这导致一个更改通过双向绑定更改另一个。为每个实例化单独的模型,您的问题将得到解决。将控件构建为具体控件(代码隐藏也是视图模型)可以消除此问题,并且在大多数情况下是MVVM最佳实践。