WPF 简易日期控件 魔改ListBox
程序员文章站
2022-05-01 20:43:59
先上截图 修正: 应该将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 ......
先上截图
修正:
应该将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="<" 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=">" 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>
上一篇: 樊哙为什么不让刘邦霸占秦三世的后宫妃嫔?
下一篇: 基于情感词典的python情感分析