I would like to change the Tag
property of TextBox
that resides inside WPF ComboBox
when IsEditable="true"
What I have tried:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="TextElement.Tag" Value="MyValue" />
</Style>
The above code does not seem to work.
What I don't want:
I don't want to redefine ControlTemplate
of ComboBox just for a single property.
Why I want to change the Tag
of TextBox
inside ComboBox
instead of using ComboBox
's Tag
property: I am using EnterKeyTraversal
class as follows:
EnterKeyTraversal.cs
:
public class EnterKeyTraversal
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void ue_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var ue = e.OriginalSource as FrameworkElement;
DependencyObject dep = ue;
while (!(dep == null || dep is DataGrid))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (!(ue is Button || ue is ListBoxItem || dep is DataGrid))
{
if (e.Key == Key.Enter)
{
if (!(ue.Tag != null && ue.Tag.ToString() == "IgnoreEnterKeyTraversal"))
{
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
private static void ue_Unloaded(object sender, RoutedEventArgs e)
{
var ue = sender as FrameworkElement;
if (ue == null) return;
ue.Unloaded -= ue_Unloaded;
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
typeof(EnterKeyTraversal), new UIPropertyMetadata(false, IsEnabledChanged));
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ue = d as FrameworkElement;
if (ue == null) return;
if ((bool)e.NewValue)
{
ue.Unloaded += ue_Unloaded;
ue.PreviewKeyDown += ue_PreviewKeyDown;
}
else
{
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
}
}
In MainWindow
:
<Window ....... helpers:EnterKeyTraversal.IsEnabled="True">
........
</Window>
On some other page:
<TextBox Tag="IgnoreEnterKeyTraversal" /> <!-- Works Fine -->
<ComboBox Tag="IgnoreEnterKeyTraversal" /><!-- Not Working -->
So, I think if I change the Tag
of TextBox
inside ComboBox
, I can stop the traversal of Enter
After digging inside the EnterKeyTraversal, I found the solution:
Here is my new EnterKeyTraversal.cs
public class EnterKeyTraversal
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
static void ue_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
var ue = e.OriginalSource as FrameworkElement;
DependencyObject dep = ue;
while (!(dep == null || dep is DataGrid))
{
dep = VisualTreeHelper.GetParent(dep);
}
DependencyObject combo = ue;
while (!(combo == null || combo is ComboBox))
{
combo = VisualTreeHelper.GetParent(combo);
}
if (!(ue is Button || ue is ListBoxItem || dep is DataGrid || (combo is ComboBox && ((ComboBox)combo).Tag.ToString() == "IgnoreEnterKeyTraversal")))
{
if (e.Key == Key.Enter)
{
if (!(ue.Tag != null && ue.Tag.ToString() == "IgnoreEnterKeyTraversal"))
{
e.Handled = true;
ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
private static void ue_Unloaded(object sender, RoutedEventArgs e)
{
var ue = sender as FrameworkElement;
if (ue == null) return;
ue.Unloaded -= ue_Unloaded;
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
typeof(EnterKeyTraversal), new UIPropertyMetadata(false, IsEnabledChanged));
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ue = d as FrameworkElement;
if (ue == null) return;
if ((bool)e.NewValue)
{
ue.Unloaded += ue_Unloaded;
ue.PreviewKeyDown += ue_PreviewKeyDown;
}
else
{
ue.PreviewKeyDown -= ue_PreviewKeyDown;
}
}
}
I'm answering on basis of your initial problem : I would like to change the Tag property of TextBox that resides inside WPF ComboBox when IsEditable="true"
Since you are trying to update a control's
property which is defined in the ControlTemplate
of ComboBox
a style
, Binding
or Trigger
is not going to work. So i used this helper class:
Helper Class:
public class ComboBoxBehave
{
public static string GetApplyTag(DependencyObject obj)
{
return (string)obj.GetValue(ApplyTagProperty);
}
public static void SetApplyTag(DependencyObject obj, string value)
{
obj.SetValue(ApplyTagProperty, value);
}
// Using a DependencyProperty as the backing store for ApplyTag. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ApplyTagProperty =
DependencyProperty.RegisterAttached("ApplyTag", typeof(string), typeof(ComboBoxBehave), new UIPropertyMetadata(null, ApplyTagChanged));
private static void ApplyTagChanged(DependencyObject DO, DependencyPropertyChangedEventArgs e)
{
var combo = DO as ComboBox;
if (combo != null)
{
combo.Loaded += new RoutedEventHandler(combo_Loaded);
}
}
static void combo_Loaded(object sender, RoutedEventArgs e)
{
var combo = sender as ComboBox;
if (combo != null)
{
var text = FindChild<TextBox>(sender as DependencyObject, "PART_EditableTextBox");
if (text != null)
{
text.Tag = GetApplyTag(combo);
}
}
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
}
XAML:
<ComboBox Width="200" Name="cmbBox1" IsEditable="True" >
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="IsEditable" Value="True">
<Setter Property="local:ComboBoxBehave.ApplyTag" Value="IgnoreEnterKeyTraversal"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
And TextBox's
Tag is set to IgnoreEnterKeyTraversal
, With a little more generic code same class can be used with different controls.
You can consider to create your own ComboBox
, something like this:
public class ComboBox : System.Windows.Controls.ComboBox
{
public static readonly DependencyProperty TextBoxTagProperty =
DependencyProperty.Register("TextBoxTag", typeof(object), typeof(ComboBox), new UIPropertyMetadata(null, new PropertyChangedCallback(OnTextBoxTagChanged)));
public object TextBoxTag
{
get { return (object)GetValue(TextBoxTagProperty); }
set { SetValue(TextBoxTagProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
FrameworkElement frameworkElement = GetTemplateChild("PART_EditableTextBox") as FrameworkElement;
frameworkElement.Tag = TextBoxTag;
}
private static void OnTextBoxTagChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
ComboBox comboBox = d as ComboBox;
FrameworkElement frameworkElement = comboBox.GetTemplateChild("PART_EditableTextBox") as FrameworkElement;
frameworkElement.Tag = args.NewValue;
}
}
Then in your XAML:
<local:ComboBox IsEditable="True" Tag="combobox tag" TextBoxTag="edit textbox tag" />
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.