Wpf ComboBox 多级正反向级联的实现

思考了一个多星期,经过反复测试验证,终于实现了Wpf ComboBox 多级正反向级联,话不多说,下面上代码:

这是一个企业管理系统示例,当然只是一个笑话。数据库有三张表,一张企业表,一张5级行政区划表,全国->省市->地市->县市->乡镇街道,还有一张注册表,标示使用者及其行政区划。为了简化示例,企业表只有2个字段,名称和地址,地址又分为2个字段,一个行政区划Id,联接行政区划表,一个行政区划下的地址。下面使用Ef Code First进行设计表类:

企业表类:

namespace WpfApp.Adress.ComboBox
{
    /// <summary>
    /// 一般情况下,可以不用继承界面更改通知接口,EF会自动通知,但计算属性并且不保存在数据库的,EF不会通知更改,
    /// 同时,既然继承了界面更改通知接口,EF就不会自动通知,需要给每个属性写更改通知。
    /// </summary>
    public class Company : NotifyPropertyChanged
    {
        public int CompanyId { get; set; }
        private string companyName;
        public string CompanyName
        {
            get => companyName;
            set
            {
                companyName = value;
                RaisePropertyChanged("CompanyName");
            }
        }
        private string adress;
        public string Adress
        {
            get => adress;
            set
            {
                if (adress != value)
                {
                    adress = value;
                    RaisePropertyChanged("Adress");
                    RaisePropertyChanged("UpAdress");
                }
            }
        }
        private int districtId;
        public int DistrictId
        {
            get => districtId;
            set
            {
                if (districtId != value)
                {
                    districtId = value;
                    RaisePropertyChanged("DistrictId");
                    RaisePropertyChanged("UpAdress");
                }
            }
        }
        public virtual District District { get; set; }
        public string UpAdress //只读属性不会作为数据表字段
        {
            get
            {
                var upAdrss = "";
                District = DataBase.DbContext.Districts.Find(DistrictId);
                if (District != null)
                {
                    var d = District;
                    while (!d.Equals(DataBase.RegData.District))
                    {
                        upAdrss = d.DistrictName + upAdrss;
                        d = d.UpDistrict;
                    }
                }
                upAdrss += Adress;
                return upAdrss;
            }
        }
        public string FullAdress
        {
            get
            {
                if (DataBase.RegData.District.DistrictLevel != 1)
                {
                    return DataBase.RegData.District.DistrictName + UpAdress;
                }
                return UpAdress;
            }
        }
    }
}

行政区划表类:

namespace WpfApp.Adress.ComboBox
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;

    public class District  //行政区域表
    {
        public District()
        {
            ChildDistricts = new HashSet<District>();
            Companys = new HashSet<Company>();
            RegDatas = new HashSet<RegData>();
        }
        public int DistrictId { get; set; }
        [MaxLength(20)]
        [Required]
        public string DistrictName { get; set; }
        public byte DistrictLevel { get; set; }
        [ForeignKey("UpDistrict")]
        public int UpDistrictId { get; set; }
    
        public virtual ICollection<District> ChildDistricts { get; set; }
        public virtual District UpDistrict { get; set; }
        public virtual ICollection<Company> Companys { get; set; }
        public virtual ICollection<RegData> RegDatas { get; set; }
    }
}
注册表类:

namespace WpfApp.Adress.ComboBox
{
    using System.ComponentModel.DataAnnotations;
    public class RegData
    {
        public int RegDataId { get; set; }
        public int DistrictId { get; set; }
        [MaxLength(20)]
        [Required]
        public string RegName { get; set; }

        public virtual District District { get; set; }
    }
}

DbCotext类略,这个就标示上面三张表,名称为CompanyDbContext。

企业类有一个DataBase类是这样的:

namespace WpfApp.Adress.ComboBox
{
    public class DataBase
    {
        private static CompanyDbContext dbContext;
        public static CompanyDbContext DbContext
        {
            get
            {
                if (dbContext == null)
                    dbContext = new CompanyDbContext();
                return dbContext;
            }
        }
        private static RegData regData;
        public static RegData RegData
        {
            get
            {
                if (regData == null)
                {
                    regData = DbContext.RegDatas.Find(1);
                }
                return regData;
            }
        }
        public static bool IsFirstRun()
        {
            var reg = DbContext.RegDatas.Find(1);
            return reg == null;
        }
        /// <summary>
        /// 初始化数据库略
        /// </summary>
        public static void DataInit()
        {
            //DbContext.Districts.Add(new District() { DistrictName = "全国", DistrictLevel = 1, UpDistrictId = 1 });//1
            //DbContext.Districts.Add(new District() { DistrictName = "浙江省", DistrictLevel = 2, UpDistrictId = 1 });//2
            //。。。。。。
            //DbContext.RegDatas.Add(new RegData() { RegName = "浙江省文化厅", DistrictId = 2 });

            //DbContext.SaveChanges();
        }

    }
}
然后是关键之一,正反向级联的多个ComboBox Wpf代码:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Converters="clr-namespace:WpfApp.Adress.ComboBox.Converters"
    x:Class="WpfApp.Adress.ComboBox.AdressDistrict"
             mc:Ignorable="d" 
             d:DesignHeight="25" d:DesignWidth="110">
    <UserControl.Resources>
        <Converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </UserControl.Resources>
    <Grid>
        <StackPanel Orientation="Horizontal" Visibility="{Binding IsFourthLevel, ConverterParameter=same, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock Text="{Binding District.DistrictName}" VerticalAlignment="Center" Width="Auto"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding District.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedValuePath="DistrictId" SelectedValue="{Binding SelectedCompany.DistrictId, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Visibility="{Binding IsThirdLevel, ConverterParameter=same, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock Text="{Binding District.DistrictName}" VerticalAlignment="Center" Width="Auto"/>
            <ComboBox x:Name="comboBox" Height="25" Width="Auto" ItemsSource="{Binding District.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding ChildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding GrandchildDistricts}" DisplayMemberPath="DistrictName" SelectedValuePath="DistrictId" SelectedValue="{Binding SelectedCompany.DistrictId, Mode=TwoWay}" SelectedItem="{Binding SelectedCompany.District, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Visibility="{Binding IsSecondLevel, ConverterParameter=same, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock Text="{Binding District.DistrictName}" VerticalAlignment="Center" Width="Auto"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding District.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding ChildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding ChildDistrict.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding GrandchildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}" />
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding GrandchildDistrict.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedValuePath="DistrictId" SelectedValue="{Binding SelectedCompany.DistrictId, Mode=TwoWay}" SelectedItem="{Binding GreatgrandchildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Visibility="{Binding IsFirstLevel, ConverterParameter=same, Converter={StaticResource BooleanToVisibilityConverter}}">
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding District.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding ChildDistricts, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding ChildDistrict.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding GrandchildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding GrandchildDistrict.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedItem="{Binding GreatgrandchildDistrict, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}" />
            <ComboBox Height="25" Width="Auto" ItemsSource="{Binding GreatgrandchildDistrict.ChildDistricts}" DisplayMemberPath="DistrictName" SelectedValuePath="DistrictId" SelectedValue="{Binding SelectedCompany.DistrictId, Mode=TwoWay}" SelectedItem="{Binding SelectedCompany.District, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}"/>
        </StackPanel>
    </Grid>
</UserControl>
上面代码是根据不同注册者的行政区划级别来选择显示,当然只有县市级及以上部门才能成为注册者。
关键之一的操作类ViewModels:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity.Migrations;
using System.Linq;
namespace WpfApp.Adress.ComboBox
{
    /// <summary>
    /// 企业录入编辑
    /// </summary>
    public class CompanyEditViewModel : ViewModelBase
    {
        /// <summary>
        /// Initializes a new instance of the CompanyEditViewModel class.
        /// </summary>
        #region 构造函数
        public CompanyEditViewModel()
        {
            EditType = EditModeType.Default;
            district = DataBase.RegData?.District;
            userDistrictLevel = district.DistrictLevel;
            SelectedIndex = -1;
        }

        #endregion

        #region 私有字段

        private int selectedIndex;
        private int oldSelectedIndex;
        private Company selectedCompany;
        private Company pocodCompany = new Company();
        private CompanyDbContext DbContext = DataBase.DbContext;
        private bool IsDefaultMode
        {
            get { return EditType == EditModeType.Default; }
        }
        private bool IsSelected
        {
            get { return (SelectedCompany != null); }
        }
        private ObservableCollection<Company> companys;

        enum EditModeType
        {
            Default, //浏览(非编辑)模式
            Add,  //添加模式
            Edit    //修改模式
        }
        private EditModeType editType;
        private EditModeType EditType
        {
            get { return editType; }
            set
            {
                editType = value;
                RaisePropertyChanged("IsEnabled");
                RaisePropertyChanged("IsReadOnly");
            }
        }
        byte userDistrictLevel;
        District district;
        District childDistrict;
        District grandchildDistrict;
        District greatgrandchildDistrict;

        #endregion

        #region 绑定字段
        public District District
        {
            get
            {
                return district;
            }
        }
        public bool IsFirstLevel
        {
            get
            {
                return userDistrictLevel == 1;//部委
            }
        }
        public bool IsSecondLevel
        {
            get
            {
                return userDistrictLevel == 2;//省市
            }
        }
        public bool IsThirdLevel
        {
            get
            {
                return userDistrictLevel == 3;//地市 
            }
        }
        public bool IsFourthLevel
        {
            get
            {
                return userDistrictLevel == 4;//县市
            }
        }

        public District ChildDistrict
        {
            get
            {
                return childDistrict;
            }

            set
            {
                childDistrict = value;
                RaisePropertyChanged("ChildDistrict");
                RaisePropertyChanged("GrandchildDistricts");
            }
        }

        public District GrandchildDistrict
        {
            get
            {
                return grandchildDistrict;
            }

            set
            {
                grandchildDistrict = value;
                RaisePropertyChanged("GrandchildDistrict");
                RaisePropertyChanged("GreatgrandchildDistricts");
            }
        }

        public District GreatgrandchildDistrict
        {
            get
            {
                return greatgrandchildDistrict;
            }

            set
            {
                greatgrandchildDistrict = value;
                RaisePropertyChanged("GreatgrandchildDistrict");
            }
        }
        public IList<District> GrandchildDistricts
        {
            get
            {
                return ChildDistrict?.ChildDistricts.ToList();
            }
        }
        public IList<District> GreatgrandchildDistricts
        {
            get
            {
                return GrandchildDistrict?.ChildDistricts.ToList();
            }
        }
        public int SelectedIndex
        {
            get { return selectedIndex; }
            set
            {
                if (selectedIndex != value)
                {
                    selectedIndex = value;
                    RaisePropertyChanged("SelectedIndex");
                }
            }
        }

        public Company SelectedCompany
        {
            get
            {
                return selectedCompany;
            }
            set
            {
                if (selectedCompany != value)
                {
                    selectedCompany = value;
                    SetCurrentDistrict();
                    RaisePropertyChanged("SelectedCompany");
                    RaisePropertyChanged("isSelected");
                }
            }
        }
        public ObservableCollection<Company> Companys
        {
            get
            {
                if (companys == null)
                {
                    companys = new ObservableCollection<Company>(DbContext.Companys);
                }
                return companys;
            }
        }
        public bool IsEnabled
        {
            get { return !IsDefaultMode; }
        }
        public bool IsReadOnly
        {
            get { return IsDefaultMode; }
        }
        #endregion

        #region 私有方法

        private void SetCurrentDistrict()//实现反向级联
        {
            switch (userDistrictLevel)
            {
                case 1:
                    ChildDistrict = SelectedCompany?.District?.UpDistrict.UpDistrict.UpDistrict;
                    GrandchildDistrict = SelectedCompany?.District?.UpDistrict.UpDistrict;
                    GreatgrandchildDistrict = SelectedCompany?.District?.UpDistrict;
                    break;
                case 2:
                    ChildDistrict = SelectedCompany?.District?.UpDistrict.UpDistrict;
                    GrandchildDistrict = SelectedCompany?.District?.UpDistrict;
                    break;
                case 3:
                    ChildDistrict = SelectedCompany?.District?.UpDistrict;
                    break;
                case 4:
                    break;
            }
        }
        internal override void Add()
        {
            var newCompany = new Company();
            AddNew(newCompany);
            EditType = EditModeType.Add;
        }
        private void AddNew(Company p)
        {
            companys.Add(p);
            oldSelectedIndex = SelectedIndex;
            SelectedIndex = Companys.IndexOf(p);
            SelectedCompany = p;
        }
        internal override bool CanAdd()
        {
            return IsDefaultMode;
        }
        internal override void Edit()
        {
            BackUpItem();
            EditType = EditModeType.Edit;
        }

        private void BackUpItem()//保存原始数据
        {
            pocodCompany.CompanyId = SelectedCompany.CompanyId;
            pocodCompany.CompanyName = SelectedCompany.CompanyName;
            pocodCompany.DistrictId = SelectedCompany.DistrictId;
            pocodCompany.Adress = SelectedCompany.Adress;
        }

        internal override bool CanEdit()
        {
            return (IsDefaultMode && IsSelected);
        }
        internal override void Cancel()
        {
            if (EditType == EditModeType.Add)
            {
                Companys.Remove(SelectedCompany);
                SelectedIndex = oldSelectedIndex;
            }
            else
            {
                BackToItem();//返回原始数据
            }
            EditType = EditModeType.Default;
        }

        private void BackToItem()
        {
            if (!SelectedCompany.Equals(pocodCompany))
            {
                SelectedCompany.CompanyName = pocodCompany.CompanyName;
                SelectedCompany.DistrictId = pocodCompany.DistrictId;
                SelectedCompany.Adress = pocodCompany.Adress;
            }
        }

        internal override bool CanCancel()
        {
            return !IsDefaultMode;
        }
        internal override void Save()
        {
            DbContext.Companys.AddOrUpdate(SelectedCompany);
            DbContext.SaveChanges();
            EditType = EditModeType.Default;
        }
        internal override bool CanSave()
        {
            return !IsDefaultMode;
        }

        internal override void Delete()
        {
            DbContext.Companys.Remove(SelectedCompany);
            DbContext.SaveChanges();
            Companys.Remove(SelectedCompany);
        }
        internal override bool CanDelete()
        { return (IsDefaultMode && IsSelected); }
        internal override bool CanExit() { return IsDefaultMode; }
        internal override void Exit()
        {
            DbContext.Dispose();
            base.Exit();
        }
        #endregion

    }
}

其他的辅助类就不上代码了。

整个工程文件:


运行界面:


工程代码下载:http://download.csdn.net/download/luwq168/10195850

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页