欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF自定义TreeView控件样式实现QQ联系人列表效果

程序员文章站 2023-12-18 18:49:22
一、前言 treeview这个控件对于我来说是用得比较多的,以前做的小聊天软件(好友列表)、音乐播放器(播放列表)、类库展示器(树形类结构)等都用的是treeview...

一、前言

treeview这个控件对于我来说是用得比较多的,以前做的小聊天软件(好友列表)、音乐播放器(播放列表)、类库展示器(树形类结构)等都用的是treeview,普通的treeview并不能满足我们的需求。因此我们需要滴对treeview进行改造。下面的内容将介绍仿qq联系人treeview样式及treeview数据绑定方法。

二、treeview仿qq联系人列表

准确的说不是仿qq联系人列表,这个treeview样式作为组织架构来使用更好。废话不多说,先看效果:

WPF自定义TreeView控件样式实现QQ联系人列表效果

 2.1、基本思路

像这种联系人列表一般涉及到多层级数据,而且有很多数据是需要动态更新的,如果通过手动一条条增加数据反而更复杂,而且不方便。因此为了绑定数据方便我们使用分层模板hierarchicaldatatemplate。

分层模板中存在两种样式,一种是分组样式,一种是人员样式。不管是分组还是人员绑定的都是对象,这样我们在对象中添加一个属性来辨别是否为分组-isgrouping。

默认的treeview控件四周会有边距,因此需要设置下treeview的样式。另外鼠标经过和鼠标选中的背景色需要变化,因此还需要设置treeviewitem的样式。

根据思路,我们需要设置三个样式,treeview样式,treeviewitem样式,hierarchicaldatatemplate分层模板样式。另外为了自动计算下一级的边距,我们需要添加一个转换器indentconverter。还需要一个转换器需要将布尔类型的isgrouping转换为visibility,还需要一个转换器来对visibility取反。

这样三个样式,三个转换器。就可以实现我们上面的效果,另外还可以进行动态数据绑定。

2.2、样式代码

hierarchicaldatatemplate分层模板样式代码

<hierarchicaldatatemplate x:key="itemnode" itemssource="{binding children,mode=twoway}">
   <grid background="transparent">
    <grid.resources>
     <convert:booltovisible x:key="booltovisible"/>
     <convert:visibletoreverse x:key="visibletoreverse"/>
    </grid.resources>
    <grid minheight="30" x:name="userinfo" background="transparent" margin="-5 0 0 0" visibility="{binding visibility,elementname=groupinginfo,converter={staticresource visibletoreverse}}">
     <grid height="50" x:name="grid">
      <border background="#62acf9" width="40" height="40" cornerradius="4" horizontalalignment="left" margin="0 0 0 0">
       <textblock text="{binding surname}" fontsize="23" foreground="white" verticalalignment="center" horizontalalignment="center"/>
      </border>
      <textblock text="{binding name}" margin="50 7 0 0" fontsize="13"/>
      <textblock text="{binding info}" foreground="#808080" margin="50 30 0 0"/>
      <textblock text="{binding count,stringformat={}{0}人}" foreground="#808080" horizontalalignment="right" verticalalignment="center" margin="0 0 5 0"/>
     </grid>
    </grid>
    <stackpanel minheight="25" x:name="groupinginfo" orientation="horizontal" background="transparent" horizontalalignment="left" visibility="{binding isgrouping,converter={staticresource booltovisible}}">

     <textblock text="{binding displayname}" margin="3 0" verticalalignment="center" horizontalalignment="left"/>
    </stackpanel>
   </grid>
  </hierarchicaldatatemplate>

treeviewitem样式代码

<style x:key="defaulttreeviewitem" targettype="{x:type treeviewitem}">
   <setter property="minheight" value="25" />
   <setter property="background" value="transparent" />
   <setter property="snapstodevicepixels" value="true" />

   <setter property="margin" value="0" />
   <setter property="template">
    <setter.value>
     <controltemplate targettype="{x:type treeviewitem}">
      <controltemplate.resources>
       <convert:indentconverter x:key="indentconverter"/>
      </controltemplate.resources>
      <grid background="transparent">
       <grid.rowdefinitions>
        <rowdefinition/>
        <rowdefinition/>
       </grid.rowdefinitions>
       <border name="itembackground" background="{templatebinding background}" 
        borderbrush="{templatebinding borderbrush}" 
        borderthickness="{templatebinding borderthickness}" 
        padding="{templatebinding padding}">
        <grid background="transparent">
         <grid x:name="itemroot" margin="{binding converter={staticresource indentconverter},relativesource={relativesource templatedparent}}" background="transparent">
          <grid.columndefinitions>
           <columndefinition width="16" />
           <columndefinition width="*"/>
          </grid.columndefinitions>

          <togglebutton x:name="expander" horizontalalignment="left" clickmode="press" ischecked="{binding isexpanded, relativesource={relativesource templatedparent}}">
           <togglebutton.style>
            <style targettype="{x:type togglebutton}">
             <setter property="focusable" value="false"/>
             <setter property="width" value="16"/>
             <setter property="height" value="16"/>
             <setter property="template">
              <setter.value>
               <controltemplate targettype="{x:type togglebutton}">
                <border background="transparent" height="16" padding="5" width="16">
                 <path x:name="expandpath" data="m0,0 l0,6 l6,0 z" fill="#66645e" stroke="#66645e">
                  <path.rendertransform>
                   <rotatetransform angle="135" centery="3" centerx="3"/>
                  </path.rendertransform>
                 </path>
                </border>
                <controltemplate.triggers>
                 <trigger property="ischecked" value="true">
                  <setter property="rendertransform" targetname="expandpath">
                   <setter.value>
                    <rotatetransform angle="180" centery="3" centerx="3"/>
                   </setter.value>
                  </setter>
                  <setter property="fill" targetname="expandpath" value="#66645e"/>
                  <setter property="stroke" targetname="expandpath" value="#66645e"/>
                 </trigger>
                 <trigger property="ismouseover" value="true">
                  <setter property="stroke" targetname="expandpath" value="#66645e"/>
                  <setter property="fill" targetname="expandpath" value="#66645e"/>
                 </trigger>
                 <multitrigger>
                  <multitrigger.conditions>
                   <condition property="ismouseover" value="true"/>
                   <condition property="ischecked" value="true"/>
                  </multitrigger.conditions>
                  <setter property="stroke" targetname="expandpath" value="#66645e"/>
                  <setter property="fill" targetname="expandpath" value="#66645e"/>
                 </multitrigger>
                </controltemplate.triggers>
               </controltemplate>
              </setter.value>
             </setter>
            </style>
           </togglebutton.style>
          </togglebutton>
          <contentpresenter grid.column="1" x:name="part_header" contentsource="header" 
             horizontalalignment="stretch" >

          </contentpresenter>
         </grid>
        </grid>

       </border>
       <itemspresenter x:name="itemshost" grid.row="1"/>
      </grid>

      <controltemplate.triggers>
       <datatrigger binding="{binding isgrouping}" value="false">
        <setter property="visibility" targetname="expander" value="hidden"/>
       </datatrigger>
       <trigger property="hasitems" value="false">
        <setter property="visibility" targetname="expander" value="collapsed"/>
       </trigger>
       <trigger property="isexpanded" value="false">
        <setter property="visibility" targetname="itemshost" value="collapsed"/>
       </trigger>

       <trigger property="isselected" value="true">
        <setter property="background" targetname="itembackground" value="#fae388"/>
       </trigger>
       <multitrigger>
        <multitrigger.conditions>
         <condition property="isfocused" value="false"/>
         <condition sourcename="itembackground" property="ismouseover" value="true"/>
        </multitrigger.conditions>
        <setter property="background" value=" #fceeb9" targetname="itembackground"/>
       </multitrigger>
       <trigger property="isenabled" value="false">
        <setter property="foreground" value="{dynamicresource {x:static systemcolors.graytextbrushkey}}"/>
       </trigger>
      </controltemplate.triggers>
     </controltemplate>
    </setter.value>
   </setter>
  </style>

treeview样式代码

<style x:key="defaulttreeview" targettype="{x:type treeview}">
   <setter property="scrollviewer.cancontentscroll" value="true" />
   <setter property="virtualizingstackpanel.isvirtualizing" value="true"></setter>
   <setter property="virtualizingstackpanel.virtualizationmode" value="recycling" />
   <setter property="scrollviewer.isdeferredscrollingenabled" value="false" />
   <setter property="itemcontainerstyle" value="{staticresource defaulttreeviewitem}"></setter>
   <setter property="padding" value="0"/>
   <setter property="itemspanel">
    <setter.value>
     <itemspaneltemplate>
      <virtualizingstackpanel isitemshost="true" margin="0"/>
     </itemspaneltemplate>
    </setter.value>
   </setter>
  </style>

2.3、转换器代码

public class indentconverter : ivalueconverter
 {
  public object convert(object value, type targettype, object parameter, cultureinfo culture)
  {
   double colunwidth = 10;
   double left = 0.0;
   uielement element = value as treeviewitem;
   while (element.gettype() != typeof(treeview))
   {
    element = (uielement)visualtreehelper.getparent(element);
    if (element.gettype() == typeof(treeviewitem))
     left += colunwidth;
   }
   return new thickness(left, 0, 0, 0);
  }

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

 }

 public class booltovisible : ivalueconverter
 {
  public object convert(object value, type targettype, object parameter, cultureinfo culture)
  {
   if ((bool)value)
    return visibility.visible;
   else
    return visibility.collapsed;
  }

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

 public class visibletoreverse : ivalueconverter
 {
  public object convert(object value, type targettype, object parameter, cultureinfo culture)
  {
   if ((visibility)value == visibility.visible)
    return visibility.collapsed;
   else
    return visibility.visible;
  }

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

2.4、引用示例

 <treeview x:name="treevieworg" borderthickness="1" borderbrush="#bbb" background="transparent" width="280" height="500" margin="10" itemtemplate="{staticresource itemnode}" style="{staticresource defaulttreeview}">
   </treeview>

2.5、初始化数据源及绑定对象

public mainwindow()
  {
   initializecomponent();
   orglist = new observablecollection<orgmodel>()
   {
    new orgmodel()
    {
     isgrouping=true,
     displayname="单位名称(3/7)",
     children=new observablecollection<orgmodel>()
     {
      new orgmodel(){
       isgrouping=true,
       displayname="未分组联系人(2/4)",
       children=new observablecollection<orgmodel>()
       {
        new orgmodel(){
         isgrouping=false,
         surname="刘",
         name="刘棒",
         info="我要走向天空!",
         count=3
        }
       }
      }
     },
    }

   };
   treevieworg.itemssource = orglist;
  }

  public observablecollection<orgmodel> orglist { get; set; }

  public class orgmodel
  {
   public bool isgrouping { get; set; }
   public observablecollection<orgmodel> children { get; set; }
   public string displayname { get; set; }
   public string surname { get; set; }
   public string name { get; set; }
   public string info { get; set; }
   public int count { get; set; }
  }

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

上一篇:

下一篇: