找回密码
 立即注册

QQ登录

只需一步,快速开始

gw0506

超级版主

181

主题

4212

帖子

9049

积分

超级版主

Rank: 8Rank: 8

积分
9049

活字格认证

gw0506
超级版主   /  发表于:2010-12-30 17:04  /   查看:5869  /  回复:0
介绍
有这样一个需求,当用户双击Tab控件Header区域时, 希望可以直接编辑。对于WPF控件,提供一个ControlTemplate在加上一些Trigger就可以实现。效果如下:




代码
首先,我们需要给Tab Header设计一个ControlTemplate。类似一个TextBlock,双击进入编辑状态。 所以Xaml如下:
  1. <Setter Property="Template">
  2.                 <Setter.Value>
  3.                     <ControlTemplate TargetType="{x:Type local:EditableTabHeaderControl}">
  4.                         <Grid>
  5.                             <TextBox x:Name="PART_TabHeader" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}" Visibility="Collapsed"/>
  6.                             <TextBlock x:Name="PART_TextBlock" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}"/>
  7.                         </Grid>
  8.                         <ControlTemplate.Triggers>
  9.                             <Trigger Property="IsInEditMode" Value="True">
  10.                                 <Trigger.Setters>
  11.                                     <Setter TargetName="PART_TabHeader" Property="Visibility" Value="Visible"/>
  12.                                     <Setter TargetName="PART_TextBlock" Property="Visibility" Value="Collapsed"/>
  13.                                 </Trigger.Setters>
  14.                             </Trigger>
  15.                         </ControlTemplate.Triggers>
  16.              </ControlTemplate>
  17.         </Setter.Value>
  18. </Setter>
复制代码
接下来,我们需要定义个“EditableTabHeaderControl”类,它具有控制TextBox和TextBlock的能力。如下:
  1. namespace EditableTabHeaderDemo
  2. {
  3.     using System;
  4.     using System.Windows;
  5.     using System.Windows.Controls;
  6.     using System.Windows.Input;
  7.     using System.Windows.Threading;

  8.     /// <summary>
  9.     /// Header Editable TabItem
  10.     /// </summary>
  11.     [TemplatePart(Name = "PART_TabHeader", Type = typeof(TextBox))]
  12.     public class EditableTabHeaderControl : ContentControl
  13.     {
  14.         /// <summary>
  15.         /// Dependency property to bind EditMode with XAML Trigger
  16.         /// </summary>
  17.         private static readonly DependencyProperty IsInEditModeProperty = DependencyProperty.Register("IsInEditMode", typeof(bool), typeof(EditableTabHeaderControl));
  18.         private TextBox textBox;
  19.         private string oldText;
  20.         private DispatcherTimer timer;
  21.         private delegate void FocusTextBox();

  22.         /// <summary>
  23.         /// Gets or sets a value indicating whether this instance is in edit mode.
  24.         /// </summary>
  25.         public bool IsInEditMode
  26.         {
  27.             get
  28.             {
  29.                 return (bool)this.GetValue(IsInEditModeProperty);
  30.             }
  31.             set
  32.             {   
  33.                 if (string.IsNullOrEmpty(this.textBox.Text))
  34.                 {
  35.                     this.textBox.Text = this.oldText;
  36.                 }
  37.                
  38.                 this.oldText = this.textBox.Text;
  39.                 this.SetValue(IsInEditModeProperty, value);
  40.             }
  41.         }

  42.         /// <summary>
  43.         /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
  44.         /// </summary>
  45.         public override void OnApplyTemplate()
  46.         {
  47.             base.OnApplyTemplate();
  48.             this.textBox = this.Template.FindName("PART_TabHeader", this) as TextBox;
  49.             if (this.textBox != null)
  50.             {
  51.                 this.timer = new DispatcherTimer();
  52.                 this.timer.Tick += TimerTick;
  53.                 this.timer.Interval = TimeSpan.FromMilliseconds(1);
  54.                 this.LostFocus += TextBoxLostFocus;
  55.                 this.textBox.KeyDown += TextBoxKeyDown;
  56.                 this.MouseDoubleClick += EditableTabHeaderControlMouseDoubleClick;
  57.             }
  58.         }

  59.         /// <summary>
  60.         /// Sets the IsInEdit mode.
  61.         /// </summary>
  62.         /// <param name="value">if set to <c>true</c> [value].</param>
  63.         public void SetEditMode(bool value)
  64.         {
  65.             this.IsInEditMode = value;
  66.             this.timer.Start();
  67.         }

  68.         private void TimerTick(object sender, EventArgs e)
  69.         {
  70.             this.timer.Stop();
  71.             this.MoveTextBoxInFocus();
  72.         }

  73.         private void MoveTextBoxInFocus()
  74.         {
  75.             if (this.textBox.CheckAccess())
  76.             {
  77.                 if (!string.IsNullOrEmpty(this.textBox.Text))
  78.                 {
  79.                     this.textBox.CaretIndex = 0;
  80.                     this.textBox.Focus();
  81.                 }
  82.             }
  83.             else
  84.             {
  85.                 this.textBox.Dispatcher.BeginInvoke(DispatcherPriority.Render, new FocusTextBox(this.MoveTextBoxInFocus));
  86.             }
  87.         }

  88.         private void TextBoxKeyDown(object sender, KeyEventArgs e)
  89.         {
  90.             if (e.Key == Key.Escape)
  91.             {
  92.                 this.textBox.Text = oldText;
  93.                 this.IsInEditMode = false;
  94.             }
  95.             else if (e.Key == Key.Enter)
  96.             {
  97.                 this.IsInEditMode = false;
  98.             }
  99.         }

  100.         private void TextBoxLostFocus(object sender, RoutedEventArgs e)
  101.         {
  102.             this.IsInEditMode = false;
  103.         }

  104.         private void EditableTabHeaderControlMouseDoubleClick(object sender, MouseButtonEventArgs e)
  105.         {
  106.             if (e.LeftButton == MouseButtonState.Pressed)
  107.             {
  108.                 this.SetEditMode(true);
  109.             }
  110.         }
  111.     }
  112. }
复制代码
这里有一个问题,当控件进入编辑状态,TextBox变为可见状态时,它不能自动获得focus。一种解决办法是挂一个Timer,每1毫秒轮询一次,检查状态并控制focus。
现在就来添加一个WPF TabControl,并应用ItemContainerStyle。然后双击Header,可以编辑啦~
  1. <Window x:Class="EditableTabHeaderDemo.MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.     xmlns:local="clr-namespace:EditableTabHeaderDemo"
  5.     Title="EditableTabHeaderDemo" Height="300" Width="500">
  6.     <Window.Resources>
  7.         <Style x:Key="EditableTabHeaderControl" TargetType="{x:Type local:EditableTabHeaderControl}">
  8.                         <!-- The template specified earlier will come here !-->
  9.         </Style>
  10.         <Style x:Key="ItemContainerStyle" TargetType="TabItem">
  11.             <Setter Property="HeaderTemplate">
  12.                 <Setter.Value>
  13.                     <DataTemplate>
  14.                         <local:EditableTabHeaderControl
  15.                    Style="{StaticResource EditableTabHeaderControl}">
  16.                             <local:EditableTabHeaderControl.Content>
  17.                                 <Binding Path="Name" Mode="TwoWay"/>
  18.                             </local:EditableTabHeaderControl.Content>
  19.                         </local:EditableTabHeaderControl>
  20.                     </DataTemplate>
  21.                 </Setter.Value>
  22.             </Setter>
  23.         </Style>
  24.         <DataTemplate x:Key="ContentTemplate">
  25.             <Grid>
  26.                 <TextBlock HorizontalAlignment="Left" Text="{Binding Name}"/>
  27.                 <TextBlock HorizontalAlignment="Center" Text="{Binding City}"/>
  28.             </Grid>
  29.         </DataTemplate>
  30.     </Window.Resources>
  31.     <Grid>
  32.         <TabControl Grid.Row="0" ItemsSource="{Binding Data}" ItemContainerStyle="{StaticResource ItemContainerStyle}" ContentTemplate="{StaticResource ContentTemplate}" />
  33.     </Grid>
  34. </Window>
复制代码
许可证
本文以及示例代码文件遵循The Code Project Open License(CPOL)。

源码下载

英文链接:Header Editable Tab Control in Wpf

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部