程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 【WPF】實現QQ中的分組面板

【WPF】實現QQ中的分組面板

編輯:關於.NET

要實現的面板的效果如下圖所示:

一個面板打開了,其它的面板會自動收起。而且打開的面板會填充所有可用空間。那麼這樣的效果在WPF裡應該如何實現呢?

1. 多個面板,排成一排,感覺可以用ListBox。

2. 然後裡面的東西,點一下打開,再點一下收起。感覺就是一個Expander嘛。

3. 一個打開,其它所有的收起。可以把Expander的IsExpanded與SelectedItem綁定。

第一步:ListBox + Expander + Style

上面所有的功能都可以用現有的控件做到,沒有必要做個自定義控件,一個Style就可以搞定了。

為了讓代碼窄一點。所以分成了幾個部分。Style部分如下所示。

<ControlTemplate x:Key="ExpandListItemTemplate" TargetType="{x:Type ListBoxItem}">
   <Border BorderThickness="1">
     <Border CornerRadius="3" Padding="2,1,2,2" BorderThickness="1" BorderBrush="#FF666670">
       <Expander Header="Header" Content="{TemplateBinding Content}"
            IsExpanded="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}"/>
     </Border>
   </Border>
</ControlTemplate>
<Style x:Key="ExpandListItemStyle" TargetType="{x:Type ListBoxItem}">
   <Setter Property="Template" Value="{StaticResource ExpandListItemTemplate}"/>
</Style>
<Style x:Key="MostSimple" TargetType="{x:Type ListBox}">
   <Setter Property="SelectionMode" Value="Single"/>
   <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="ItemContainerStyle" Value="{StaticResource ExpandListItemStyle}"/>
</Style>

主體部分如下所示。

<ListBox Style="{StaticResource MostSimple}">
   <ListBoxItem>
     <ListBox>
       <Button Content="OK"/>
       <Button Content="Cancel"/>
       <Button Content="Yes"/>
       <Button Content="No"/>
     </ListBox>
   </ListBoxItem>
   <ListBoxItem>
     <ListBox>
       <CheckBox Content="Close"/>
       <CheckBox Content="Open"/>
       <CheckBox Content="Copy"/>
       <CheckBox Content="Paste"/>
     </ListBox>
   </ListBoxItem>
   <ListBoxItem>
     <ListBox>
       <RadioButton Content="Stay"/>
       <RadioButton Content="Leave"/>
       <RadioButton Content="Stay"/>
       <RadioButton Content="Leave"/>
     </ListBox>
   </ListBoxItem>
</ListBox>

我們來看一下效果。

看上去差不多了,點一下如何?

完了,打開的Expander沒有填充。由於ListBoxItem默認是放在VirtualizedStackPanel中的,所以裡面的Item都是向上對齊,不填充。我的 第一感覺就是用個會填充的當ItemsPanel就可以了。記得DockPanel有LastChildFill的功能。試試。

<ItemsPanelTemplate x:Key="ExpandListItemsPanelTemplate">
   <DockPanel/>
</ItemsPanelTemplate>
<Style x:Key="ExpandListItemStyle" TargetType="{x:Type ListBoxItem}">
   <Setter Property="DockPanel.Dock" Value="Top"/>
   <Setter Property="Template" Value="{StaticResource ExpandListItemTemplate}"/>
</Style>
<Style x:Key="MostSimple" TargetType="{x:Type ListBox}">
   <Setter Property="SelectionMode" Value="Single"/>
   <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="ItemContainerStyle" Value="{StaticResource ExpandListItemStyle}"/>
   <Setter Property="ItemsPanel" Value="{StaticResource ExpandListItemsPanelTemplate}"/>
</Style>

看看效果。

寒,還沒有點就展開了。早該想到的。

想了半天如何動態地改變Last Child是哪個。但是沒有想出啥好辦法,一個方法是把Item一個個取出來重新放回去。這也太變態了。再想想 好像也沒有可用的Panel了。完了,又要寫一個自定義Panel了。還好這個Panel的功能不復雜。

這個面板要做的就是把其中的一個item填充。其它的就按其期望的最小大小放上去。順序不能亂就可以了。

想了一個簡單的邏輯。就是LargestChildFill,找出最大的Child,讓它填充剩下的所有空間。

/// <summary>
/// The logic is very simple. Make the largest child fill.
/// </summary>
public class GroupPanel : Panel
{
   private UIElement largestChild;
   private double totalHeight;

   protected override Size MeasureOverride(Size availableSize)
   {
     totalHeight = 0;
     double width = 0;

     largestChild = null;
     foreach (UIElement child in Children)
     {
       child.Measure(availableSize);

       if (largestChild == null || child.DesiredSize.Height >= largestChild.DesiredSize.Height)
       {
         largestChild = child;
       }

       totalHeight += child.DesiredSize.Height;
       if (child.DesiredSize.Width > width)
       {
         width = child.DesiredSize.Width;
       }
     }

     return new Size(width, totalHeight);
   }

   protected override Size ArrangeOverride(Size finalSize)
   {
     double yOffset = 0;
     foreach (UIElement child in Children)
     {
       if (child == largestChild)
       {
         double finalHeight = child.DesiredSize.Height + finalSize.Height - totalHeight;
         child.Arrange(new Rect(0, yOffset, finalSize.Width, finalHeight));
         yOffset += finalHeight;
       }
       else 
       {
         child.Arrange(new Rect(0, yOffset, finalSize.Width, child.DesiredSize.Height));
         yOffset += child.DesiredSize.Height;
       }
     }

     return finalSize;
   }
}

然後把ItemsPanelTemplate改一下。

<ItemsPanelTemplate x:Key="ExpandListItemsPanelTemplate">
   <c:GroupPanel/>
</ItemsPanelTemplate> 

搞定。看下效果。

就是想要的效果。目前這些面板可以全部折疊起來,如果你想讓至少一個展開。一個簡單的方法就是。當展開後,把Expander的Header裡的 ToggleButton禁用。

再常見一些的需求就是這個面板的展開過程要有動畫什麼的。這裡就不再演示了。

目前為到止,寫在博客裡的自定義Panel就有仨了。其實我想說的是,WPF自帶的幾個Panel的功能實在是很基本的。了解如何實現自定義 Panel,對於實現一些常見的功能還是很有幫助的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved