当前位置: 移动技术网 > IT编程>开发语言>.net > WPF 简易日期控件 魔改ListBox

WPF 简易日期控件 魔改ListBox

2020年05月10日  | 移动技术网IT编程  | 我要评论

宝贝咱成亲,初中音乐教学工作总结,重生之异界血皇

先上截图

 

修正:

应该将settime方法修改为,行号为207行开始修改

     var nk = day_of_week(year, month, 1);
            if (nk == 0)
                nk = 7;
            for (var i = 0; i < nk-1; i++)
            {
                time.add(new basetime());
            }

 

 

源代码

整体过程是魔改listbox,整体是放在用户控件中,关于日期的计算是来自这位博友的

抱歉没有过多注释,原本也只是一时兴起,多多见谅

 

1 修改listbox的模板

将listbox的模板设为一个三层结果的模板

1 左右日期调整

2 星期显示

3 当月显示

其中左右日期调整为按钮,并实现是命令。星期显示使用数据提供者进行枚举绑定,当月显示即为普通的lisbox面板

 

 

2修改listitem

大部分的改动都是这里的,一开始想用子项样式选择器或者子项模板选择器来进行动态改变,不过发现不太适合,后来改为数据触发器动态改变底层矩形颜色。

整体是一个checkbox,利用其属性来完成选择过程。

选择过程是使用栈,保证只有最多两个选择项

 

 

3添加依赖属性

保证某些数据能够被获取,不过没有实现路由事件。所以本控件依旧是一个玩具,不能被直接使用于真实项目

 

代码中命名不太规范,请凑乎看。

xaml代码

<usercontrol x:class="日期控件.uc_datetime"
             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" 
             xmlns:local="clr-namespace:日期控件"
             xmlns:sys="clr-namespace:system;assembly=mscorlib"
             mc:ignorable="d" 
             d:designheight="450" d:designwidth="800" x:name="uc_datetime_"    >
    <usercontrol.resources>
        <local:autowidth x:key="autowidth"/>
        <objectdataprovider x:key="week" objecttype="{x:type sys:enum}"  methodname="getvalues"   >
            <objectdataprovider.methodparameters>
                <x:type  typename="local:basedatetime"/>
            </objectdataprovider.methodparameters>
        </objectdataprovider>
        <local:basedate x:key="time"   />
        <local:baseselectstyle x:key="select"/>
        <local:datetimecombo x:key="datetime"/>
        <local:selectdatetimelist x:key="sdt"/>
        <style targettype="local:uc_datetime">
            <setter property="selectdatetime"  value="{binding source={staticresource time},  path=selectdatetimelist}"/>
        </style>
    </usercontrol.resources>

    <grid x:name="gridpanel" tag="{binding elementname=lb,path=tag}">
        <listbox  margin="0,0,0,10" x:name="lb"    selectionmode="single"     borderbrush="silver" borderthickness="0,1,0,1"  >
            <listbox.itemssource>
                <multibinding  converter="{staticresource datetime}">
                    <binding elementname="uc_datetime_" path="setyear"/>
                    <binding elementname="uc_datetime_" path="setmonth"/>
                    <binding   source="{staticresource time}"/>
                </multibinding>
            </listbox.itemssource>
            <listbox.itemspanel>
                <itemspaneltemplate>
                    <wrappanel    width="{binding relativesource={relativesource mode=findancestor,ancestorlevel=1,ancestortype=scrollcontentpresenter}, path=actualwidth}"   itemwidth="{binding relativesource={relativesource mode=self}, path=width,converter={staticresource autowidth}}" />
                </itemspaneltemplate>
            </listbox.itemspanel>
            <listbox.template>
                <controltemplate targettype="listbox">
                    <grid>
                        <grid.rowdefinitions>
                            <rowdefinition height="auto"/>
                            <rowdefinition height="auto"/>
                            <rowdefinition height="*"/>
                        </grid.rowdefinitions>
                        <grid grid.row="0">
                            <grid.columndefinitions>
                                <columndefinition width="*"/>
                                <columndefinition width="*" />
                                <columndefinition width="*"/>
                            </grid.columndefinitions>
                            <button x:name="previousbutton"   grid.column="0" background="transparent" command="{binding path=previousclick,  source={staticresource time}}"   borderthickness="0" content="&lt;" horizontalcontentalignment="left" >
                                <button.commandparameter>
                                    <multibinding converter="{staticresource datetime}" converterparameter="1">
                                        <binding elementname="year" mode="twoway"/>
                                        <binding elementname="month" mode="twoway"/>
                                    </multibinding>
                                </button.commandparameter>
                            </button>
                            <textblock grid.column="1" horizontalalignment="center"  >
                                <run x:name="year" text="{binding elementname=uc_datetime_,path=setyear}"/>
                                <run text="年"/>
                                <run x:name="month" text="{binding elementname=uc_datetime_,path=setmonth}"/>
                                <run text="月"/>
                            </textblock>
                            <button x:name="nextbutton" grid.column="2"  command="{binding source={staticresource time}, path=nextclick}"  background="transparent" borderthickness="0" content="&gt;" horizontalcontentalignment="right">
                                <button.commandparameter>
                                    <multibinding converter="{staticresource datetime}" converterparameter="1">
                                        <binding elementname="year" mode="twoway"/>
                                        <binding elementname="month" mode="twoway"/>
                                    </multibinding>
                                </button.commandparameter>
                            </button>
                        </grid>
                        <itemscontrol   grid.row="1"    itemssource="{binding source={staticresource week}}" >
                            <itemscontrol.itemspanel>
                                <itemspaneltemplate>
                                    <wrappanel   width="{binding relativesource={relativesource mode=findancestor,ancestorlevel=1,ancestortype=itemscontrol}, path=actualwidth}"   itemwidth="{binding relativesource={relativesource mode=self}, path=width,converter={staticresource autowidth}}" />
                                </itemspaneltemplate>
                            </itemscontrol.itemspanel>
                            <itemscontrol.itemtemplate>
                                <datatemplate>
                                    <textblock text="{binding }" horizontalalignment="center"/>
                                </datatemplate>
                            </itemscontrol.itemtemplate>
                        </itemscontrol>
                        <border grid.row="2" margin="{templatebinding margin}"   borderthickness="{templatebinding borderthickness}"  borderbrush="{templatebinding borderbrush}">
                            <scrollviewer padding="3" verticalscrollbarvisibility="hidden">
                                <itemspresenter snapstodevicepixels="{templatebinding snapstodevicepixels}"/>
                            </scrollviewer>
                        </border>
                    </grid>
                </controltemplate>
            </listbox.template>
            <listbox.itemcontainerstyle>
                <style targettype="listboxitem">
                    <setter property="template">
                        <setter.value>
                            <controltemplate targettype="listboxitem">
                                <controltemplate.resources>
                                    <solidcolorbrush color="{binding elementname=uc_datetime_, path=selectitemlistbackground}" x:key="listselectedcolor"/>
                                    <solidcolorbrush color="{binding elementname=uc_datetime_, path=selectitembackground}" x:key="selectedcolor"/>
                                </controltemplate.resources>
                                <checkbox  x:name="cb"  ischecked="{binding itemisselected,mode=twoway}"   clickmode="release" command="{binding  click,source={staticresource time }}"   commandparameter="{ binding relativesource={relativesource mode=self}, path=datacontext}"  >
                                    <checkbox.template>
                                        <controltemplate>
                                            <grid>
                                                <grid>
                                                    <grid.columndefinitions>
                                                        <columndefinition/>
                                                        <columndefinition/>
                                                    </grid.columndefinitions>
                                                    <rectangle margin="0,2,0,2" grid.column="0" fill="{binding elementname=uc_datetime_, path=selectitemlistbackground }" x:name="leftbackground"  visibility="collapsed"/>
                                                    <rectangle margin="0,2,0,2" grid.column="1" fill="{binding elementname=uc_datetime_, path=selectitemlistbackground }" x:name="rightbackground"  visibility="collapsed"/>
                                                    <ellipse grid.columnspan="2" x:name="bd" width="{binding elementname=uc_datetime_, path=backgroundellipessize}" height="{binding elementname=uc_datetime_, path=backgroundellipessize}"/>
                                                </grid>
                                                <textblock   x:name="txt"    text="{binding time}" fontsize="{binding elementname=uc_datetime_, path=datetimefontsize}"  horizontalalignment="center" verticalalignment="center"/>
                                            </grid>
                                            <controltemplate.triggers>
                                                <datatrigger binding="{binding time}" value="0">
                                                    <setter property="isenabled" value="false"/>
                                                    <setter property="text" targetname="txt" value=""/>
                                                </datatrigger>
                                                <multidatatrigger >
                                                    <multidatatrigger.conditions>
                                                        <condition binding="{binding itemisselected}" value="true"/>
                                                    </multidatatrigger.conditions>
                                                    <setter property="fill" targetname="bd" value="{binding elementname=uc_datetime_, path=selectitembackground }" />
                                                </multidatatrigger>
                                                <datatrigger binding="{binding itemisselected}" value="false">
                                                    <setter property="fill" value="white"   targetname="bd"/>
                                                </datatrigger>
                                                <datatrigger binding="{binding backgroundshowmode}" value="none">
                                                    <setter targetname="leftbackground" property="visibility" value="collapsed"/>
                                                    <setter targetname="rightbackground" property="visibility" value="collapsed"/>
                                                </datatrigger>
                                                <datatrigger binding="{binding backgroundshowmode}" value="right">
                                                    <setter targetname="leftbackground" property="visibility" value="collapsed"/>
                                                    <setter targetname="rightbackground" property="visibility" value="visible"/>
                                                </datatrigger>
                                                <datatrigger binding="{binding backgroundshowmode}" value="left">
                                                    <setter targetname="leftbackground" property="visibility" value="visible"/>
                                                    <setter targetname="rightbackground" property="visibility" value="collapsed"/>
                                                </datatrigger>
                                                <datatrigger binding="{binding backgroundshowmode}" value="both">
                                                    <setter targetname="leftbackground" property="visibility" value="visible"/>
                                                    <setter targetname="rightbackground" property="visibility" value="visible"/>
                                                    <setter property="visibility" targetname="bd"  value="hidden"/>
                                                </datatrigger>
                                            </controltemplate.triggers>
                                        </controltemplate>
                                    </checkbox.template>
                                </checkbox>
                            </controltemplate>
                        </setter.value>
                    </setter>
                </style>
            </listbox.itemcontainerstyle>
        </listbox>
    </grid>
</usercontrol>

 

xaml.cs页面

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
using system.windows;
using system.windows.controls;
using system.windows.data;
using system.windows.documents;
using system.windows.input;
using system.windows.media;
using system.windows.media.imaging;
using system.windows.navigation;
using system.windows.shapes;

namespace 日期控件
{
    /// <summary>
    /// uc_datetime.xaml 的交互逻辑
    /// </summary>
    public partial class uc_datetime : usercontrol
    {
        public uc_datetime()
        {
            initializecomponent();
        }
        public static readonly dependencyproperty datetimefontsizeproperty = dependencyproperty.register("datetimefontsize", typeof(int), typeof(uc_datetime), new propertymetadata(27));

        public int datetimefontsize
        {
            get => convert.toint32(getvalue(datacontextproperty));
            set => setvalue(datacontextproperty, value);
        }

        public static readonly dependencyproperty backgroundellipesstretchproperty = dependencyproperty.register("backgroundellipesstretch", typeof(stretch), typeof(uc_datetime), new propertymetadata(stretch.uniformtofill));

        public stretch backgroundellipesstretch
        {
            get => (stretch)getvalue(backgroundellipesstretchproperty);
            set => setvalue(backgroundellipesstretchproperty, value);
        }

        public static readonly dependencyproperty selectitembackgroundproperty = dependencyproperty.register("selectitembackground", typeof(solidcolorbrush), typeof(uc_datetime), new propertymetadata(new solidcolorbrush(colors.red)));

        public solidcolorbrush selectitembackground
        {
            get => (solidcolorbrush)getvalue(selectitembackgroundproperty);
            set => setvalue(selectitembackgroundproperty, value);
        }

        public static readonly dependencyproperty backgroundellipessizeproperty = dependencyproperty.register("backgroundellipessize", typeof(double), typeof(uc_datetime), new propertymetadata(25.0));

        public double backgroundellipessize
        {
            get => convert.todouble(getvalue(backgroundellipessizeproperty));
            set => setvalue(backgroundellipessizeproperty, value);
        }

        public static readonly dependencyproperty selectitemlistbackgroundproperty = dependencyproperty.register("selectitemlistbackground", typeof(solidcolorbrush), typeof(uc_datetime), new propertymetadata(new solidcolorbrush(colors.red)));
      
        public solidcolorbrush selectitemlistbackground
        {
            get => (solidcolorbrush)getvalue(selectitemlistbackgroundproperty);
            set => setvalue(selectitemlistbackgroundproperty, value);
        }

        public static readonly dependencyproperty backgroundshowmodeproperty = dependencyproperty.register("backgroundshowmode", typeof(showmode), typeof(uc_datetime), new propertymetadata(showmode.none));

        public showmode backgroundshowmode
        {
            get => (showmode)getvalue(backgroundshowmodeproperty);
            set => setvalue(backgroundshowmodeproperty, value);
        }
        public static readonly dependencyproperty setyearproperty = dependencyproperty.register("setyear", typeof(int), typeof(uc_datetime), new propertymetadata(datetime.now.year));

        public int setyear
        {
            get => convert.toint32(getvalue(setyearproperty));
            set => setvalue(setyearproperty, value);
        }
        public static readonly dependencyproperty setmonthproperty = dependencyproperty.register("setmonth", typeof(int), typeof(uc_datetime), new propertymetadata(datetime.now.month));

        public int setmonth
        {
            get => convert.toint32(getvalue(setmonthproperty));
            set => setvalue(setmonthproperty, value);
        }
        //datetime.parse(datetime.now.tostring("yyyy-mm-dd"))
        public static readonly dependencyproperty selectdatetimepropery = dependencyproperty.register("selectdatetime", typeof(list<datetime>), typeof(uc_datetime), new propertymetadata( new list<datetime>() { }));

        public list<datetime> selectdatetime
        {
            get => (list<datetime>)getvalue(selectdatetimepropery);
            set => setvalue(selectdatetimepropery, value);
        }
    }
}

 

 

最重要的视觉模型类

using system;
using system.collections.generic;
using system.collections.objectmodel;
using system.componentmodel;
using system.linq;
using system.runtime.compilerservices;
using system.text;
using system.threading.tasks;
using system.windows.controls;
using system.windows.documents;
using system.windows.input;

namespace 日期控件
{
    public class clickcommand : icommand
    {
        public event eventhandler canexecutechanged;

        public bool canexecute(object parameter)
        {
            return true;
        }

        public void execute(object parameter)
        {
            if (parameter != null)
                action?.invoke(parameter);
            else
                noparameteraction?.invoke();
        }
        private action noparameteraction;
        private action<object> action;
        public clickcommand(action<object> acion)
        {
            this.action = acion;
        }
        public clickcommand(action action)
        {
            this.noparameteraction = action;
        }
    }
    public enum basedatetime
    {
        周一,
        周二,
        周三,
        周四,
        周五,
        周六,
        周日,
    }
    public enum showmode
    {
        left,
        right,
        both,
        none
    }
    public class basetime : inotifypropertychanged
    {
        private int _time;
        public int time { get => _time; set { _time = value; onvaluechanged(); } }

        private bool _itemisselected;
        public bool itemisselected { get => _itemisselected; set { _itemisselected = value; onvaluechanged(); onselecchanged?.invoke(this); } }

        private bool _ischangtemplate;
        public bool ischangtemplate { get => _ischangtemplate; set { _ischangtemplate = value; onvaluechanged(); } }

        public event propertychangedeventhandler propertychanged;

        protected void onvaluechanged([callermembername]string name = "") => propertychanged?.invoke(this, new propertychangedeventargs(name));

        public action<basetime> onselecchanged;

        private showmode _showmode=showmode.none;

        public showmode backgroundshowmode
        {
            get => _showmode;
            set
            {
                _showmode = value;
                onvaluechanged();
            }
        }
    }
   
    public class basedate : inotifypropertychanged
    {
        public event propertychangedeventhandler propertychanged;

        protected void onvaluechanged([callermembername]string name = "") => propertychanged?.invoke(this, new propertychangedeventargs(name));

        public observablecollection<basetime> time { get; set; }
        public int year { get; set; }
        public int month { get; set; }
//单选 public icommand click { get => new clickcommand(new action<object>(onclick)); }
//日期调整 public icommand nextclick { get => new clickcommand(new action<object>(onnextclick)); } int monthcount = 0; private void onnextclick(object n) { updatetime(n,1); month++; monthcount++; if (month== 13) { month = 1; this.year += 1; } settime(this.year, this.month); updatetime(n); } public timespan selecttime { get;set; } private object _selectdatetimelist; public object selectdatetimelist { get => _selectdatetimelist; set { _selectdatetimelist = value; onvaluechanged(); } } public list<datetime> setselecttime() { var ls = new list<datetime>(); if (stackbasetimelist.count == 2) { if (year == 0 && month == 0) { ls.add(new datetime(year2, month2, stackbasetimelist.last().time)); ls.add(new datetime(year2, month2, stackbasetimelist.first().time)); } else { ls.add(new datetime(year, month, stackbasetimelist.last().time)); ls.add(new datetime(year, month, stackbasetimelist.first().time)); } } return ls; } private void updatetime(object n,int i=0) { var arr = n as object[]; var r1 = arr[0] as run; var r2 = arr[1] as run; if (i == 1) { year = convert.toint32(r1.text); month = convert.toint32(r2.text); return; } r1.text = year.tostring(); r2.text = month.tostring(); } public icommand previousclick { get => new clickcommand(new action<object>(onpreviousclick)); } private void onpreviousclick(object n) { updatetime(n,1); month--; monthcount--; if (month == 0) { month = 12; this.year -= 1; } settime(this.year, this.month); updatetime(n); } private void onclick(object obj) { if(obj!=null) queueupdate(obj as basetime); } private int year2, month2; public void settime(int year,int month) { year2 = year; month2 = month; time.clear(); stackbasetimelist.clear(); setnorm(); clear(); var day = days_of_month(year, month); var nk = day_of_week(year, month, 1); for (var i = 0; i < nk; i++) { time.add(new basetime()); } for (var i=0;i<day;i++) { time.add(new basetime() { time = i + 1 }); } } ////返回这个月一共有多少天 int days_of_month(int year, int month) { //存储平年每月的天数 int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (2 == month && datetime.isleapyear(year)) return 29; // 如果是闰年2月,返回29天 else return month_days[month - 1]; //正常返回 } int days_of_year(int year, int month, int day) { int i; int days = 0; for (i = 1; i < month; i++) { days += days_of_month(year, i); } return days + day; } //返回这一天从公元元年算起是第几天 int get_days(int year, int month, int day) { int days = days_of_year(year, month, day); int temp = year - 1; return temp * 365 + temp / 4 - temp / 100 + temp / 400 + days; } int day_of_week(int year, int month, int day) { return get_days(year, month, day) % 7; } public basedate() { stackbasetimelist = new stack<basetime>(); time = new observablecollection<basetime>(); for (var i = 0; i < 31; i++) if (i == 5) time.add(new basetime() { time = i + 1, ischangtemplate = true }); else time.add(new basetime() { time = i + 1 }); } private void setbackground() { setnorm(); var f = stackbasetimelist.first(); var l = stackbasetimelist.last(); var start = time.indexof(l); var end = time.indexof(f); if(l.time<f.time) for(var i=start;i<end+1;i++) { var item = time[i]; item.backgroundshowmode = showmode.both; if (item.time == l.time) { item.backgroundshowmode = showmode.right; } if (item.time == f.time) { item.backgroundshowmode = showmode.left; } } selectdatetimelist = setselecttime(); } private void setnorm() { foreach(var item in time) { item.backgroundshowmode = showmode.none; } } private stack<basetime> stackbasetimelist; private void clear() { foreach (var item in time) item.itemisselected = false; stackbasetimelist.clear(); }
//原先是队列,不过不对,名字没改,现在是栈表 private void queueupdate(basetime bt) { if (bt.itemisselected) { if (stackbasetimelist.count < 2) { if (stackbasetimelist.count == 1) { var l = stackbasetimelist.last(); if (bt.time > l.time) stackbasetimelist.push(bt); else { clear(); l.itemisselected = true; stackbasetimelist.push(l); } } else stackbasetimelist.push(bt); } else { var f = stackbasetimelist.first(); var l = stackbasetimelist.last(); if (bt.time > l.time) { var pop = stackbasetimelist.pop(); pop.itemisselected = false; stackbasetimelist.push(bt); } else { clear(); setnorm(); l.itemisselected = true; stackbasetimelist.push(l); } } } else { var f = stackbasetimelist.first(); var l = stackbasetimelist.last(); if (l.time == bt.time) { clear(); setnorm(); } else if (f.time == bt.time) { stackbasetimelist.pop(); } else if(bt.time<=l.time) { clear(); setnorm(); } } if (stackbasetimelist.count == 2) setbackground(); } } }

 

各种转换器

using system;
using system.collections.generic;
using system.globalization;
using system.linq;
using system.text;
using system.threading.tasks;
using system.windows.data;

namespace 日期控件
{
    public class autowidth : ivalueconverter
    {
        public object convert(object value, type targettype, object parameter, cultureinfo culture)
        {
            var width = system.convert.todouble(value) / 7;

            return width;
        }

        public object convertback(object value, type targettype, object parameter, cultureinfo culture)
        {
            var width = system.convert.todouble(value) * 7;

            return width;
        }
    }
    public class datetimecombo : imultivalueconverter
    {
        public object convert(object[] values, type targettype, object parameter, cultureinfo culture)
        {
            if (parameter != null)
            {
             
                return new object[2] { values[0],values[1]};

            }
                var t = values[2] as basedate;
                t.settime((int)values[0], (int)values[1]);
        
                return t.time;
            
        }

        public object[] convertback(object value, type[] targettypes, object parameter, cultureinfo culture)
        {
            throw new notimplementedexception();
        }
    }

    public class selectdatetimelist : ivalueconverter
    {

        public object convert(object value, type targettype, object parameter, cultureinfo culture)
        {
            var bt = value as basedate;
             return value;
        }

        public object convertback(object value, type targettype, object parameter, cultureinfo culture)
        {
            throw new notimplementedexception();
        }
    }

}

 

显示页面xaml

<window x:class="日期控件.mainwindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:日期控件"
        mc:ignorable="d"
        title="mainwindow" height="450" width="800">
    <grid>
        <grid.rowdefinitions>
            <rowdefinition height="auto"/>
            <rowdefinition height="auto"/>
            <rowdefinition height="*"/>
        </grid.rowdefinitions>
        <textblock text="{binding elementname=qq,path=selectdatetime[0]}"/>
        <textblock grid.row="1" text="{binding elementname=qq,path=selectdatetime[1]}"/>
        <local:uc_datetime  grid.row="2" margin="0,30,0,0" backgroundellipessize="35" setmonth="4"  x:name="qq" datetimefontsize="20" selectitembackground="burlywood" selectitemlistbackground="aqua" backgroundellipesstretch="none"/>
    </grid>
</window>

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网