当前位置:   article > 正文

WPF自定义控件-根据内容生成菜单_wpf 菜单控件

wpf 菜单控件

效果图:

控件:支持左边点击,右边内容滚到顶部,右边鼠标中键滚动,左边菜单栏跟着变化。

1.控件样式代码App.xaml

  1. <Application
  2. x:Class="HT.ControlWPF.App"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:local="clr-namespace:HT.ControlWPF"
  6. StartupUri="MainWindow.xaml">
  7. <Application.Resources>
  8. <Style TargetType="{x:Type local:CustomMenuScrollViewer}">
  9. <Setter Property="Template">
  10. <Setter.Value>
  11. <ControlTemplate TargetType="{x:Type local:CustomMenuScrollViewer}">
  12. <Grid>
  13. <Grid.ColumnDefinitions>
  14. <ColumnDefinition Width="Auto" />
  15. <ColumnDefinition Width="*" />
  16. </Grid.ColumnDefinitions>
  17. <!-- 菜单栏 -->
  18. <ScrollViewer
  19. Grid.Row="0"
  20. Grid.Column="0"
  21. HorizontalScrollBarVisibility="Auto"
  22. VerticalScrollBarVisibility="Auto">
  23. <ItemsControl x:Name="PART_Mune">
  24. <ItemsControl.ItemsPanel>
  25. <ItemsPanelTemplate>
  26. <StackPanel Orientation="Vertical" />
  27. </ItemsPanelTemplate>
  28. </ItemsControl.ItemsPanel>
  29. <ItemsControl.ItemTemplate>
  30. <DataTemplate>
  31. <Grid>
  32. <RadioButton
  33. MinWidth="120"
  34. HorizontalContentAlignment="Stretch"
  35. Command="{Binding CheckedCommand}"
  36. CommandParameter="{Binding ElementName=PART_Function_Content}"
  37. Content="{Binding Title}"
  38. Cursor="Hand"
  39. GroupName="rd"
  40. IsChecked="{Binding IsSeleted, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
  41. <RadioButton.Resources>
  42. <Style TargetType="{x:Type RadioButton}">
  43. <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
  44. <Setter Property="BorderThickness" Value="1" />
  45. <Setter Property="Padding" Value="10,5,10,5" />
  46. <Setter Property="Margin" Value="2,0,2,0" />
  47. <Setter Property="Template">
  48. <Setter.Value>
  49. <ControlTemplate TargetType="{x:Type RadioButton}">
  50. <Border
  51. x:Name="content_Border"
  52. Margin="{TemplateBinding Margin}"
  53. Padding="{TemplateBinding Padding}">
  54. <ContentPresenter
  55. x:Name="contentPresenter"
  56. HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  57. VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  58. Focusable="False"
  59. RecognizesAccessKey="True"
  60. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
  61. </Border>
  62. <ControlTemplate.Triggers>
  63. <Trigger Property="IsMouseOver" Value="True">
  64. <Setter Property="Foreground" Value="#379aff" />
  65. </Trigger>
  66. <DataTrigger Binding="{Binding IsSeleted}" Value="True">
  67. <Setter Property="Foreground" Value="#fff" />
  68. <Setter TargetName="content_Border" Property="Background" Value="#379aff" />
  69. <Setter TargetName="content_Border" Property="CornerRadius" Value="0 10 10 0" />
  70. </DataTrigger>
  71. </ControlTemplate.Triggers>
  72. </ControlTemplate>
  73. </Setter.Value>
  74. </Setter>
  75. </Style>
  76. </RadioButton.Resources>
  77. </RadioButton>
  78. </Grid>
  79. </DataTemplate>
  80. </ItemsControl.ItemTemplate>
  81. </ItemsControl>
  82. </ScrollViewer>
  83. <!-- 功能内容 -->
  84. <ScrollViewer
  85. x:Name="PART_Function_Content"
  86. Grid.Row="0"
  87. Grid.Column="1">
  88. <ScrollViewer.Resources>
  89. <Style TargetType="Label">
  90. <Setter Property="FontSize" Value="16" />
  91. <Setter Property="Margin" Value="5,2,0,2" />
  92. <Setter Property="Template">
  93. <Setter.Value>
  94. <ControlTemplate TargetType="{x:Type Label}">
  95. <Grid Margin="{TemplateBinding Margin}">
  96. <Grid.ColumnDefinitions>
  97. <ColumnDefinition Width="Auto" />
  98. <ColumnDefinition Width="*" />
  99. </Grid.ColumnDefinitions>
  100. <Rectangle
  101. Width="4"
  102. Height="18"
  103. Margin="0,0,5,0"
  104. Fill="#379aff" />
  105. <ContentPresenter
  106. Grid.Column="1"
  107. HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  108. VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
  109. RecognizesAccessKey="True"
  110. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
  111. </Grid>
  112. </ControlTemplate>
  113. </Setter.Value>
  114. </Setter>
  115. </Style>
  116. <Style TargetType="{x:Type GroupBox}">
  117. <Setter Property="Template">
  118. <Setter.Value>
  119. <ControlTemplate TargetType="{x:Type GroupBox}">
  120. <Grid SnapsToDevicePixels="true">
  121. <Grid.RowDefinitions>
  122. <RowDefinition Height="Auto" />
  123. <RowDefinition Height="Auto" />
  124. </Grid.RowDefinitions>
  125. <Border x:Name="Header" Grid.Row="0">
  126. <StackPanel>
  127. <ContentPresenter
  128. Name="header"
  129. ContentSource="Header"
  130. RecognizesAccessKey="True"
  131. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
  132. Visibility="Collapsed" />
  133. <Label HorizontalAlignment="Left" Content="{Binding ElementName=header, Path=Content}" />
  134. <Border Height="1" Background="#eeeeee" />
  135. </StackPanel>
  136. </Border>
  137. <ContentPresenter
  138. Grid.Row="1"
  139. Margin="{TemplateBinding Padding}"
  140. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
  141. </Grid>
  142. </ControlTemplate>
  143. </Setter.Value>
  144. </Setter>
  145. </Style>
  146. </ScrollViewer.Resources>
  147. </ScrollViewer>
  148. </Grid>
  149. </ControlTemplate>
  150. </Setter.Value>
  151. </Setter>
  152. </Style>
  153. </Application.Resources>
  154. </Application>

2.控件业务代码CustomMenuScrollViewer.cs

  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. namespace HT.ControlWPF
  7. {
  8. [TemplatePart(Name = PART_Function_Content, Type = typeof(ScrollViewer))]
  9. [TemplatePart(Name = PART_Mune, Type = typeof(StackPanel))]
  10. public class CustomMenuScrollViewer : Control
  11. {
  12. public const string PART_Function_Content = nameof(PART_Function_Content);
  13. public const string PART_Mune = nameof(PART_Mune);
  14. /// <summary>
  15. /// 菜单栏目录
  16. /// </summary>
  17. private List<MuneInfo> muneInfos = new List<MuneInfo>();
  18. /// <summary>
  19. /// 是否加载
  20. /// </summary>
  21. private bool isLoaded = false;
  22. /// <summary>
  23. /// 需要更新菜单
  24. /// </summary>
  25. private bool hasUpdateMune = true;
  26. /// <summary>
  27. /// 需要更新内容 为2则更新
  28. /// </summary>
  29. private int hasUpdateContentOffset = 0;
  30. /// <summary>
  31. /// 内容控件
  32. /// </summary>
  33. private ScrollViewer content_ScrollViewer = null;
  34. /// <summary>
  35. /// 菜单控件
  36. /// </summary>
  37. private ItemsControl mune = null;
  38. static CustomMenuScrollViewer()
  39. {
  40. DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomMenuScrollViewer), new FrameworkPropertyMetadata(typeof(CustomMenuScrollViewer)));
  41. }
  42. public override void OnApplyTemplate()
  43. {
  44. content_ScrollViewer = this.GetTemplateChild(PART_Function_Content) as ScrollViewer;
  45. mune = this.GetTemplateChild(PART_Mune) as ItemsControl;
  46. content_ScrollViewer.Content = this.Content;
  47. content_ScrollViewer.Loaded += ScrollViewer_Loaded;
  48. content_ScrollViewer.ScrollChanged += PART_Function_Content_ScrollChanged;
  49. base.OnApplyTemplate();
  50. }
  51. /// <summary>
  52. /// 内容绑定
  53. /// </summary>
  54. [Bindable(true)]
  55. public object Content
  56. {
  57. get { return GetValue(ContentProperty); }
  58. set { SetValue(ContentProperty, value); }
  59. }
  60. public static readonly DependencyProperty ContentProperty =DependencyProperty.Register(nameof(Content),typeof(object),typeof(CustomMenuScrollViewer),new FrameworkPropertyMetadata(null));
  61. private void RadioButton_Checked(MuneInfo selectedDataContext)
  62. {
  63. if (!hasUpdateMune) return;
  64. hasUpdateContentOffset = 0;
  65. content_ScrollViewer.ScrollToVerticalOffset(selectedDataContext.StartPoint);
  66. }
  67. private void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
  68. {
  69. if (isLoaded) return;
  70. //找出有Name的GroupBox
  71. var group_boxs = ((Panel)this.content_ScrollViewer.Content).Children.OfType<GroupBox>().Where(s => !string.IsNullOrWhiteSpace(s.Name)).ToList();
  72. for (int i = 0; i < group_boxs.Count; i++)
  73. {
  74. var key = group_boxs[i].Name;
  75. var title = group_boxs[i].Header.ToString();
  76. //计算GroupBox在ScrollViewer的开始位置和结束位置
  77. double endPoint = 0;
  78. double startPoint = 0;
  79. for (int j = 0; j <= i; j++)
  80. {
  81. endPoint += group_boxs[j].ActualHeight;
  82. if (j == i) continue;
  83. startPoint += group_boxs[j].ActualHeight;
  84. }
  85. muneInfos.Add(new MuneInfo()
  86. {
  87. PageKey = key,
  88. IsSeleted = false,
  89. Title = title,
  90. StartPoint = startPoint,
  91. EndPoint = endPoint
  92. });
  93. }
  94. foreach (var item in muneInfos)
  95. {
  96. item.Checked += RadioButton_Checked;
  97. }
  98. mune.ItemsSource = muneInfos;
  99. }
  100. private void PART_Function_Content_ScrollChanged(object sender, ScrollChangedEventArgs e)
  101. {
  102. if (++hasUpdateContentOffset >= 2)
  103. {
  104. var control = (ScrollViewer)sender;
  105. double offset = control.VerticalOffset;
  106. var itemz = muneInfos.Where(s => s.StartPoint <= offset).OrderByDescending(s => s.StartPoint).FirstOrDefault();
  107. if (itemz != null)
  108. {
  109. hasUpdateMune = false;
  110. itemz.IsSeleted = true;
  111. hasUpdateMune = true;
  112. }
  113. }
  114. }
  115. }
  116. }

3.菜单模型类MuneInfo

  1. using Prism.Commands;
  2. using Prism.Mvvm;
  3. using System;
  4. using System.Windows.Input;
  5. namespace HT.ControlWPF
  6. {
  7. public class MuneInfo : BindableBase
  8. {
  9. public event Action<MuneInfo> Checked;
  10. private bool isSeleted;
  11. /// <summary>
  12. /// 是否选中
  13. /// </summary>
  14. public bool IsSeleted
  15. {
  16. get
  17. {
  18. return isSeleted;
  19. }
  20. set
  21. {
  22. isSeleted = value;
  23. RaisePropertyChanged(nameof(IsSeleted));
  24. }
  25. }
  26. /// <summary>
  27. /// 导航地址
  28. /// </summary>
  29. public string PageKey { get; set; }
  30. /// <summary>
  31. /// 开始位置
  32. /// </summary>
  33. public double StartPoint { get; set; }
  34. /// <summary>
  35. /// 结束位置
  36. /// </summary>
  37. public double EndPoint { get; set; }
  38. private string title;
  39. /// <summary>
  40. /// 标题
  41. /// </summary>
  42. public string Title
  43. {
  44. get
  45. {
  46. return title;
  47. }
  48. set
  49. {
  50. title = value;
  51. RaisePropertyChanged(nameof(Title));
  52. }
  53. }
  54. public ICommand CheckedCommand
  55. {
  56. get
  57. {
  58. return new DelegateCommand(() =>
  59. {
  60. Checked?.Invoke(this);
  61. });
  62. }
  63. }
  64. }
  65. }

4.使用

  1. <Window
  2. x:Class="HT.ControlWPF.MainWindow"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:HT.ControlWPF"
  7. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  8. Title="MainWindow"
  9. Width="800"
  10. Height="450"
  11. FontSize="14"
  12. mc:Ignorable="d">
  13. <local:CustomMenuScrollViewer>
  14. <local:CustomMenuScrollViewer.Content>
  15. <StackPanel>
  16. <GroupBox x:Name="groupBox1" Header="功能1标题">
  17. <Border Height="200" />
  18. </GroupBox>
  19. <GroupBox x:Name="groupBox2" Header="功能2标题">
  20. <Border Height="200" />
  21. </GroupBox>
  22. <GroupBox x:Name="groupBox3" Header="功能3标题">
  23. <Border Height="200" />
  24. </GroupBox>
  25. <GroupBox x:Name="groupBox4" Header="功能4标题">
  26. <Border Height="200" />
  27. </GroupBox>
  28. <GroupBox x:Name="groupBox5" Header="功能5标题">
  29. <Border Height="200" />
  30. </GroupBox>
  31. </StackPanel>
  32. </local:CustomMenuScrollViewer.Content>
  33. </local:CustomMenuScrollViewer>
  34. </Window>

注:

1.因为最后那个内容点击第二次才能选中,所以做了一些特殊的处理,还有自己在ItemControl上找不到CheckBox,用了CheckedCommand来触发事件Checked,代码实现有点奇怪,但是功能是已经实现了的,看看那位朋友有好的建议,方便的话和我聊下好的想法。

2.该控件所有代码已经贴出来,可以可以直接使用, 把命名空间改掉就OK。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号