【问题标题】:WPF Multiple ViewModels and Multiple UserCotnrolsWPF 多视图模型和多用户控件
【发布时间】:2015-08-24 20:32:43
【问题描述】:

所以我有一个页面,它有一个使用多个用户控件的 TabControl(每个用户控件代表一个 TabItem 的内容)。

我在两个用户控件中都有一个 ComboBox,它们共享相同的 ItemsSource (OrganizationSource) 和 SelectedValue(OrganizationSelected) 属性,

然而,我似乎无法绑定到 EventDetails UserControl 内的 ComboBox,但它在 OrganizationDetails UserControl 内完美运行。

OutreachMVVM 设置为选项卡所在的父页面的 DataContext。我仍然必须为用户控件本身设置数据上下文才能使其正常工作。

我只需要弄清楚如何为EventDetails 内的ComboBox 设置绑定。我看到了一些关于依赖属性的东西,但我不明白。我以为我可以将EventDetails 内的 ComboBox 的绑定设置为与OrganizationDetails 内的 ComboBox 相同,但事实并非如此。

internal class OutreachMVVM : ViewModelBase
{
    public OutreachMVVM()
    {

        EventDetails = new EventDetailsVMB();   
        OrganizationDetails = new OrganizationDetailsVMB();
    }

    public EventDetailsVMB EventDetails { get; set; }
    public OrganizationDetailsVMB OrganizationDetails { get; set; }
}

EventDetailsVMB:

class EventDetailsVMB : ViewModelBase
{
    private string _eventTypeSelected;
    private string _zipSelected;

    private readonly UserListTableAdapter _userListTableAdapter = new UserListTableAdapter();
    private readonly EventTypeListTableAdapter _eventTypeListTableAdapter = new EventTypeListTableAdapter();
    private readonly CityListTableAdapter _cityListTableAdapter = new CityListTableAdapter();
    private readonly LocationInfoByZipTableAdapter _locationInfoByZipTableAdapter = new LocationInfoByZipTableAdapter();

    public string User { get; set; }
    public string EventName { get; set; }
    public string Location { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string County { get; set; }
    public string ServiceArea { get; set; }

    //Set EventType CombBox
    public ObservableCollection<string> EventTypeSource
    {
        get
        {
            var eventTypeList = _eventTypeListTableAdapter.GetEventTypeList();
            var eventTypeSource = new ObservableCollection<string>();
            eventTypeSource.AddRange(from DataRow row in eventTypeList.Rows select row.ItemArray[0].ToString());
            return eventTypeSource;
        }
    }

    //Set User ComboBox
    public ObservableCollection<string> UserSource
    {
        get
        {
            var userList = _userListTableAdapter.GetUserList();
            var userSource = new ObservableCollection<string>();

            foreach (var username in Enumerable.Where(userList, username => username.Username == Environment.UserName))
            {

                User = username.FullName;
            }

            userSource.AddRange(from DataRow row in userList.Rows select row.ItemArray[0].ToString());
            OnPropertyChanged("User");
            return userSource;
        }
    }

    //Set City RadAutoCompleteBox
    public ObservableCollection<string> CitySource
    {
        get
        {
            var cityList = _cityListTableAdapter.GetCityList();
            var citySource = new ObservableCollection<string>();
            citySource.AddRange(from DataRow row in cityList.Rows select row.ItemArray[0].ToString());
            return citySource;
        }
    } 


    public string EventTypeSelected
    {
        get { return _eventTypeSelected; }
        set
        {
            _eventTypeSelected = value;
            OnPropertyChanged("EventTypeSelected");
        }
    }

    public string ZipSelected 
    { 
        get { return _zipSelected;  }
        set
        {
            _zipSelected = value;
            var locationInfo = _locationInfoByZipTableAdapter.GetLocationInfoByZip(_zipSelected);
            if (locationInfo.Rows.Count != 0)
            {
                City = locationInfo.Rows[0].ItemArray[0].ToString();
                State = locationInfo.Rows[0].ItemArray[1].ToString();
                County = locationInfo.Rows[0].ItemArray[2].ToString();
                ServiceArea = locationInfo.Rows[0].ItemArray[3].ToString();
            }
            else if (ZipSelected.Length == 5) {}
            else
            {
                City = "";
                State = "TX";
                County = null;
                ServiceArea = null;
            }
            OnPropertyChanged("City");
            OnPropertyChanged("State");
            OnPropertyChanged("County");
            OnPropertyChanged("ServiceArea");
        } 
    }
}

OrganizationDetailsVMB:

class OrganizationDetailsVMB : ViewModelBase
{

    private string _organizationName;
    private string _street1;
    private string _street2;
    private string _city;
    private string _state;
    private string _zip;
    private string _county;
    private string _serviceArea;

    private bool _cbo;
    private bool _fbo;
    private bool _mo;
    private bool _sbo;
    private bool _sno;

    private readonly OrganizationListTableAdapter _organizationListTableAdapter = new OrganizationListTableAdapter();
    private readonly OrgByNameTableAdapter _orgByNameTableAdapter = new OrgByNameTableAdapter();
    private readonly OrgTypeByOrgNameTableAdapter _orgTypeByOrgNameTableAdapter = new OrgTypeByOrgNameTableAdapter();

    public string OrganizationSelected
    {
        get { return _organizationName; }
        set
        {
            _organizationName = value;
            var organizationQueryResults = _orgByNameTableAdapter.GetOrganizationByName(_organizationName);
            var orgTypeQueryResults = _orgTypeByOrgNameTableAdapter.GetOrgTypeByName(_organizationName);
            if (organizationQueryResults.Rows.Count != 0)
            {
                OrgStreet1Value = organizationQueryResults.Rows[0].ItemArray[1].ToString();
                OrgStreet2Value = organizationQueryResults.Rows[0].ItemArray[2].ToString();
                OrgCityValue = organizationQueryResults.Rows[0].ItemArray[3].ToString();
                OrgStateValue = organizationQueryResults.Rows[0].ItemArray[4].ToString();
                OrgZipValue = organizationQueryResults.Rows[0].ItemArray[5].ToString();
                OrgCountyValue = organizationQueryResults.Rows[0].ItemArray[6].ToString();
                OrgServiceAreaValue = organizationQueryResults.Rows[0].ItemArray[7].ToString();
                CBO = Convert.ToBoolean(orgTypeQueryResults.Rows[0].ItemArray[1]);
                FBO = Convert.ToBoolean(orgTypeQueryResults.Rows[0].ItemArray[2]);
                SBO = Convert.ToBoolean(orgTypeQueryResults.Rows[0].ItemArray[3]);
                MO = Convert.ToBoolean(orgTypeQueryResults.Rows[0].ItemArray[4]);
                SNO = Convert.ToBoolean(orgTypeQueryResults.Rows[0].ItemArray[5]);
            }
            else
            {
                OrgStreet1Value = "";
                OrgStreet2Value = "";
                OrgCityValue = "";
                OrgStateValue = "";
                OrgZipValue = "";
                OrgCountyValue = "";
                OrgServiceAreaValue = "";
                CBO = false;
                FBO = false;
                SBO = false;
                MO = false;
                SNO = false;
            }
        }
    }

    public ObservableCollection<string> OrganizationSource
    {
        get
        {
            var organizationList = _organizationListTableAdapter.GetOrganizationList();
            var organizationSource = new ObservableCollection<string>();
            organizationSource.AddRange(from DataRow row in organizationList.Rows select row.ItemArray[0].ToString());
            return organizationSource;
        }
    }

    public string OrgStreet1Value
    {
        get { return _street1; }
        set
        {
            if (_street1 != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgStreet1Value" });
                _street1 = value;
                OnPropertyChanged("OrgStreet1Value");
            }
        }
    }

    public string OrgStreet2Value
    {
        get { return _street2; }
        set
        {
            if (_street2 != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgStreet2Value" });
                _street2 = value;
                OnPropertyChanged("OrgStreet2Value");
            }
        }
    }

    public string OrgCityValue
    {
        get { return _city; }
        set
        {
            if (_street1 != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgCityValue" });
                _city = value;
                OnPropertyChanged("OrgCityValue");
            }
        }
    }

    public string OrgStateValue
    {
        get { return _state; }
        set
        {
            if (_state != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgStateValue" });
                _state = value;
                OnPropertyChanged("OrgStateValue");
            }
        }
    }

    public string OrgZipValue
    {
        get { return _zip; }
        set
        {
            if (_zip != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgZipValue" });
                _zip = value;
                OnPropertyChanged("OrgZipValue");
            }
        }
    }

    public string OrgCountyValue
    {
        get { return _county; }
        set
        {
            if (_county != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgCountyValue" });
                _county = value;
                OnPropertyChanged("OrgCountyValue");
            }
        }
    }

    public string OrgServiceAreaValue
    {
        get { return _serviceArea; }
        set
        {
            if (_serviceArea != value)
            {
                Validator.ValidateProperty(value,
                    new ValidationContext(this, null, null) { MemberName = "OrgServiceAreaValue" });
                _serviceArea = value;
                OnPropertyChanged("OrgServiceAreaValue");
            }
        }
    }

    public bool CBO
    {
        get { return _cbo; }
        set
        {
            _cbo = value;
            OnPropertyChanged("CBO");
        }
    }

    public bool FBO
    {
        get { return _fbo; }
        set
        {
            _fbo = value;
            OnPropertyChanged("FBO");
        }
    }

    public bool SBO
    {
        get { return _sbo; }
        set
        {
            _sbo = value;
            OnPropertyChanged("SBO");
        }
    }

    public bool MO
    {
        get { return _mo; }
        set
        {
            _mo = value;
            OnPropertyChanged("MO");
        }
    }

    public bool SNO
    {
        get { return _sno; }
        set
        {
            _sno = value;
            OnPropertyChanged("SNO");
        }
    }
}

EventDetailsTab:

<TabItem Header="Event Details" x:Name="EventDetailsTab"
            Style="{StaticResource TabStyle}"
            DataContext="{Binding EventDetails}">
    <eventTabs:_1_EventDetailsTab />
</TabItem>

组织详情选项卡:

<TabItem Header="Organization" x:Name="OrganizationTab"
            Style="{StaticResource TabStyle}"
            DataContext="{Binding OrganizationDetails}">
    <eventTabs:_2_OrganizationTab />
</TabItem>

正如我所说的,绑定总体上完美无缺,但我想引用一个与 OrganizationDetails 关联的绑定,用于驻留在 EventDetailsTab 和 OrganizationDetailsTab 中的控件。

该项目的代码如下...

<ComboBox Name="OrgNameComboBox" ItemsSource="{Binding OrganizationSource}"
                SelectedValue="{Binding OrganizationSelected,
                                        Mode=TwoWay,
                                        UpdateSourceTrigger=PropertyChanged}" />

【问题讨论】:

  • 您想要这样的东西吗:当您在Combobox 中选择OrganizationA 时,OrganizationDetailsTabEventDetailsTab 都更改了所选项目的基础 (OrganizationA) 或有 2 个组合框2 选项卡在您选择其中一项后具有 完全分离 的行为???
  • @RAM 是正确的。

标签: c# wpf mvvm data-binding


【解决方案1】:

您将事件详细信息选项卡的 DataContext 设置为 {Binding EventDetails},将组织选项卡的 DataContext 设置为 {Binding OrganizationDetails},但 ComboBox 绑定到的 OrganizationSourceOrganizationSelected 字段仅存在于OrganizationDetailsVMB 类。一个快速的技巧是将您的事件详细信息 ComboBox 更改为指向正确的位置,并将 RelativeSource 绑定回 TabItem 的 DataContext 并再次向下指向 OrganizationDetails:

<ComboBox Name="OrgNameComboBox" ItemsSource="{Binding OrganizationSource}"
        DataContext="{Binding RelativeSource={RelativeSource AncestorType=TabControl}, Path=DataContext.OrganizationDetails}" 
        SelectedValue="{Binding OrganizationSelected,
        Mode=TwoWay,
        UpdateSourceTrigger=PropertyChanged}" />

老实说,尽管我认为您需要清理架构。您的视图模型似乎与您的底层数据模型而不是视图紧密耦合,以至于您甚至可以在 getter 中访问表适配器,这意味着 A)您无法进行会话管理,B)您的视图性能现在将被您的 DAL 限制,您将开始遇到性能问题,并且 C) 尝试引入任何类型的多线程将在您的 ORM 中导致各种形式的访问冲突。

【讨论】:

  • 我对这个 MVVM 概念相当陌生。我试图找到一种方法来拥有一个主虚拟机并拥有可以包含在主视图模型中的单独的视图模型库。原因是为了让代码看起来更干净。
【解决方案2】:

A)

当您希望在 AppMulti Window 上获得一些数据(如组合框信息)时,这种方式非常有用:

创建 3 个ViewModel(全部继承自INotifyPropertyChanged)。

  1. OrganizationViewModel包括 OrganizationSourceOrganizationSelected
  2. OrganizationDetailsViewModelWHITHOUT OrganizationSourceOrganizationSelected
  3. OrganizationEventsViewModelWHITHOUT OrganizationSourceOrganizationSelected

现在您可以创建一个名为 GeneralData 的简单 Static 类并在其中创建一个 static 属性为 OrganizationViewModel 类型,如下所示:

public static class GeneralData{
   public static OrganizationViewModel Organization {get;set;}
}

App.xaml.cs 中,用OrganizationViewModel 的新实例填充GeneralData 类的Organization 属性。


好的……现在我们已经准备好开始播放所需的一切了……

每次您要填写ComboBox 时,都应该像这样使用GeneralData 类的Organization [静态属性]:

<ComboBox Name="OrgNameComboBox"
          DataSource="{Binding Source={x:Static GeneralData.Organization}}"
          ItemsSource="{Binding Source={x:Static GeneralData.Organization.OrganizationSource}}"
          SelectedValue="{Binding Source={x:Static GeneralData.Organization.OrganizationSelected,
                                  Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

并且每次您想检查在代码隐藏中选择了 Combobox 的哪个项目时(例如在 ViewModels 内),您可以像这样轻松地检查它:

var selectedOrganisation = GeneralData.Organization.OrganizationSelected;

注意:我建议您在关闭包含 ComboBox 的窗口之前运行此代码(以确保没有任何对静态属性的活动引用):

BindingOperations.ClearAllBindings(OrgNameComboBox);

B)

如果您只有 1 个窗口包含 2 个 UserControl,则阻止 static 属性,而不是上述方式使用这种方式:

创建 3 个ViewModel(全部继承自INotifyPropertyChanged)。

  1. OrganizationViewModel包括 OrganizationSourceOrganizationSelected
  2. OrganizationDetailsViewModelWHITHOUT OrganizationSourceOrganizationSelected
  3. OrganizationEventsViewModelWHITHOUT OrganizationSourceOrganizationSelected

现在轻松实现这个场景:

Window1.xaml.cs:

Window1.DataSource= new OrganizationViewModel();

EventUserControl.xaml.cs:

EventUserControl.DataSource= new OrganizationEventsViewModel();

OrganizationDetailsUserControl.xaml.cs:

EventUserControl.DataSource= new OrganizationDetailsViewModel();

现在在EventUserControlOrganizationDetailsUserControl 中创建一个DependencyProperty,以给它们SelectedItemComboBox。您的属性类型应为string。您可以基于this tutorial 创建DependencyPropertys。 例如,在 UserControls 中为您的 DependencyPropertys 使用此名称:SelectedOrganisation

好的,将Combobox 放入Window1.xaml(不要放入任何UserControl)。 现在我们从外部的UserControls 填充这些SelectedOrganisations,如下面的代码。

Window1.xaml

<uc.OrganizationDetailsUserControl 
    SelectedOrganisation="{Binding ElementName=OrgNameComboBox, Path=SelectedItem}"/>

<uc.EventUserControl
    SelectedOrganisation="{Binding ElementName=OrgNameComboBox, Path=SelectedItem}"/>

现在您在UserControl 中拥有ComboBox 中的SelectedItem,您可以在UserControls 中使用它,并使用SelectedOrganisation 作为方法参数调用ViewModels 的一些方法。

【讨论】:

    【解决方案3】:

    我走的路线是MVVM Light Toolkit

    【讨论】:

      猜你喜欢
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多