Execute a command when selecting same entry again in ComboBox using mvvm - wpf

I have Combobox in my wpf 'mvvm' app.
The Items in my ComboBox are: 'a' , 'b', 'c' , 'Browse Folder' , 'd'
Here is what i am trying to do:
(Keyboard) When i press Enter on the last Browse Folder entry I have to execute command (Cmd1) to open File Browser.
(Keyboard) - Closing & re-opening combo-box and hit press Enter on Browser Folder should re-execute the command (Cmd1).
What I tried:
If I write a Data Template to comboBox items as buttons, the button command only works with mouse click and not with keyboard Enter key.
<ComboBox.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.Cmd1, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}" Background="Transparent" Content="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
If i write the command (Cmd1) logic in SelectedIndex/SelectionChanged the it only executes one time , and I have to select a different value and Browse Folder again in drop-down to re-execute.
If i attach my Cmd1 logic to DropDownClosed then the last selected entry (Browse Folder) keeps executing every-time i open-&-close drop-down list.
How can i execute a command if i select the same entry multiple times in ComboBox in mvvm! Thanks a lot

You could use an attached behaviour that handles the KeyDown event for the specific item:
public class KeyDownBehavior
{
public static readonly DependencyProperty EnterCommandProperty = DependencyProperty.RegisterAttached(
"EnterCommand",
typeof(ICommand),
typeof(KeyDownBehavior),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnSet)));
public static void SetEnterCommand(UIElement element, ICommand value) =>
element.SetValue(EnterCommandProperty, value);
public static ICommand GetEnterCommand(UIElement element) =>
(ICommand)element.GetValue(EnterCommandProperty);
private static void OnSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement element = (UIElement)d;
element.KeyDown += OnKeyDown;
}
private static void OnKeyDown(object sender, KeyEventArgs e)
{
ICommand command = GetEnterCommand((UIElement)sender);
if (command != null)
command.Execute(null);
}
}
Sample XAML markup:
<ComboBox>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="Browse Folder">
<Setter Property="local:KeyDownBehavior.EnterCommand"
Value="{Binding DataContext.Cmd1,
RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
<s:String>a</s:String>
<s:String>b</s:String>
<s:String>c</s:String>
<s:String>Browse Folder</s:String>
<s:String>d</s:String>
</ComboBox>

Related

Changing another control's property in some control's Trigger with "dynamic" name

I am trying to change my labels style dynamically on my forms.
The behaviour I want is: Every time a textbox called 'txtName', for instance, gets Focused, it should search for a Label Control named 'lblName' and change its FontWeight property to "Bold".
The same for a textbox called 'txtBirthday' and a label called 'lblBirthday', where 'txt' stands for TextBox and lbl for Label.
Every textbox has a NAME and a prefix "txt" and a prefix "lbl" for its corresponding label, but if the textbox doesnt find a correspoding label it should do nothing.
In other words, every time a Textbox get focused on the form, it should search for the label "responsable" for its description and hightlight it (changing its font weight to bold) so the form will be more user frendly. That way the user wont get confused which textbox he is typing in.
I have a peace of code that maybe a good start point, but I dont know how to work with non-static control names.
<Style TargetType="{x:Type Label}">
<Style.Triggers>
<!-- Here is how we bind to another control's property -->
<DataTrigger Binding="{Binding IsFocused, ElementName=txtUser}" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<!-- Here is the 'override' content -->
</DataTrigger>
</Style.Triggers>
</Style>
As mentioned in the comments above, the technique of searching for and pattern matching element names as the basis for applying visual behaviour is not robust. For example, what happens when you make a typo and use "lbel" instead of "lbl"? Or what happens if you later decide to replace all Labels with TextBlocks - do you still annotate their names with a prefix of "lbl" to preserve the behaviour? Another downside to using code to change visuals - is now understanding the behaviour of your UI from reading XAML alone becomes much harder since properties are being changed behind the scenes. WPF has many built in ways which should be preferred over this approach. If you are interested in alternative implementations, just ask we are here to help :)
That being said, if must use this approach, here is what your attached behaviour would look like:
C#
public static class FontWeightFocusedHelper
{
private static readonly List<Label> Labels = new List<Label>();
public static void SetChangeFontWeightOnTextBoxFocused(Label label, bool value)
{
label.SetValue(ChangeFontWeightOnTextBoxFocusedProperty, value);
}
public static bool GetChangeFontWeightOnTextBoxFocused(Label label)
{
return (bool) label.GetValue(ChangeFontWeightOnTextBoxFocusedProperty);
}
public static readonly DependencyProperty ChangeFontWeightOnTextBoxFocusedProperty =
DependencyProperty.RegisterAttached("ChangeFontWeightOnTextBoxFocused", typeof (bool),
typeof (FontWeightFocusedHelper),
new FrameworkPropertyMetadata(OnChangeFontWeightOnTextBoxFocusedPropertyChanged));
private static void OnChangeFontWeightOnTextBoxFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox)
{
var textBox = (TextBox) d;
// Make sure to use a WeakEventManager here otherwise you will leak ...
textBox.GotFocus += OnTextBoxGotFocusChanged;
textBox.LostFocus += OnTextBoxLostFocusChanged;
return;
}
if (d is Label)
{
// Make sure to store WeakReferences here otherwise you will leak ...
Labels.Add((Label)d);
return;
}
throw new InvalidOperationException("ChangeFontWeightOnTextBoxFocused can only be set on TextBox and Label types.");
}
private static void OnTextBoxLostFocusChanged(object sender, RoutedEventArgs e)
{
SetMatchingLabelFontWeight(sender as TextBox, FontWeights.Regular);
}
private static void OnTextBoxGotFocusChanged(object sender, RoutedEventArgs e)
{
SetMatchingLabelFontWeight(sender as TextBox, FontWeights.Bold);
}
private static void SetMatchingLabelFontWeight(TextBox textBox, FontWeight fontWeight)
{
if (textBox != null)
{
// Suggest adding a property for LabelPrefix and TextBoxPrefix too, use them here
var label = Labels.Where(l => !String.IsNullOrEmpty(l.Name))
.Where(l => l.Name.Replace("lbl", "txt") == textBox.Name)
.FirstOrDefault();
if (label != null)
{
label.FontWeight = fontWeight;
}
}
}
}
XAML
<StackPanel >
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="l:FontWeightFocusedHelper.ChangeFontWeightOnTextBoxFocused" Value="True" />
</Style>
<Style TargetType="{x:Type Label}">
<Setter Property="l:FontWeightFocusedHelper.ChangeFontWeightOnTextBoxFocused" Value="True" />
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<Label x:Name="lblOne" VerticalAlignment="Center" Content="First Name"/>
<TextBox x:Name="txtOne" Width="300" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label x:Name="lblTwo" VerticalAlignment="Center" Content="Last Name" />
<TextBox x:Name="txtTwo" Width="300" VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
Hope this helps!
You can have all gotfocus go to the same event. The sender is passed to the event so you can get the name of the sender. In code behind you can use variable and logic that is not available in XAML.

Silverlight how to promote selected item of listbox when using buttons within datatemplates

Within a Listbox control I have a Data Template which consists of text and a button. Given the nature of Silverlight/WPF when I click on the button within the listbox item the button event is trapped before the listbox item is selected. Therefore if I am trying to pass the record ID of the selected listbox item I am currently only able to do so by first clicking and selecting the listbox item and then clicking on the button.
Is there a way to promote the selection of the listbox item so that when the listbox items are created I have the ability to click on the button within the listbox item and some event (selectionChanged ?) is invoked which would allow me to capture the selected record id and use it for some other action ( pass as a parameter in a method etc). I'm using Simple MVVM toolkit for this implementation so I was wondering if this could be handled in the viewModel or if I would need to handle this in the controls code behind and then push the selection to the viewModel.
The listbox control is presented as:
<ListBox x:Name="ResultListBox"
HorizontalAlignment="Stretch"
Background="{x:Null}"
Grid.Row="1"
BorderThickness="0" HorizontalContentAlignment="Stretch"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}"
ItemsSource="{Binding SearchResults[0].Results}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource ListBoxStyle1}">
<ListBox.ItemTemplate>
<DataTemplate>
<dts:TypeTemplateSelector Content="{Binding}" HorizontalContentAlignment="Stretch">
<!-- Template 1 -->
<formatter:TypeTemplateSelector.CFSTemplate>
<DataTemplate>
<qr:ucIndex_Product />
</DataTemplate>
</formatter:TypeTemplateSelector.CFSTemplate>
<!-- Template 2 -->
<formatter:TypeTemplateSelector.PersonTemplate>
<DataTemplate>
<qr:ucIndex_Person />
</DataTemplate>
</formatter:TypeTemplateSelector.PersonTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Within the datatemplate (user control) resides the button along with a number of other fields. I'll omit that code for the time being unless requested.
Thanks in advance!
Put this in your ListBox.Resources
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
And this in the Code Behind
protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
{
ListBoxItem item = (ListBoxItem)sender;
item.IsSelected = true;
}
You could use the following code as well which doesn't use code-behind, however it only keeps the ListBoxItem selected for as long as it has KeyBoard focus. Once focus leaves, the item becomes unselected
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
EDIT
Since Silverlight doesn't have EventSetters, you can use the ListBox's Loaded event and add the following to your code behind:
private void ResultListBox_Loaded(object sender, RoutedEventArgs e)
{
ListBox list = (ListBox)sender;
list.GotFocus += ResultListBox_GotFocus;
}
void ResultListBox_GotFocus(object sender, RoutedEventArgs e)
{
var item = FindAncester<ListBoxItem>((DependencyObject)e.OriginalSource);
if (item != null) item.IsSelected = true;
}
T FindAncester<T>(DependencyObject current)
where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
This captures the Focus event for the ListBox, takes the control that triggered the focus event and traverses up the visual tree to find the ListBoxItem objects, and sets it's Selected value to true.
Rachel's solution works great. The one issue I did find in this approach was that it does place total focus on the selected item. As a result the user would be required to double click within the control to place focus on other items such as selectable text or other button. After working with this a bit more I discovered you can also resolve this by setting the listbox selected items to the data context of the object you are clicking on etc. This works well as allows you to set this to any UI object within the control.
ListBox.SelectedItem = ((HyperlinkButton)sender).DataContext;
In this example I had Hyperlink buttons within the data template. Clicking on them would then set the focus to the selected listbox item.
rlcrews got it right! Use DataContext:
ObservableCollection<Employee> employees1;
...
listBox1.ItemsSource = employees1;
...
//DataTemplate in ListBox has a button with following event
private void bnPromoteEmployee_Click(object sender, RoutedEventArgs e)
{
Employee emp1 = (Employee)((Button)sender).DataContext;
emp1.Promote();
}

How to select all the text when the edit textbox in a DataGridTemplateColumn receives focus?

I'm trying to get a DataGridTemplateColumn to behave identically to a TextColumn
when the cell goes into edit mode (Press F2), the user can immediately start typing in the new value
by default, existing text content is selected - so that you can set new values easily
Got the first one done ; however selecting all the text isn't working. As mentioned by a number of posts, tried hooking into the GotFocus event and selecting all the text in code-behind. This worked for a standalone textbox ; however for a Textbox which is the edit control for a TemplateColumn, this doesn't work.
Any ideas?
Code Sample:
<Window.Resources>
<Style x:Key="HighlightTextBoxStyle" TargetType="{x:Type TextBox}">
<EventSetter Event="GotFocus" Handler="SelectAllText"/>
<EventSetter Event="GotMouseCapture" Handler="SelectAllText"/>
<Setter Property="Background" Value="AliceBlue"/>
</Style>
<DataTemplate x:Key="DefaultTitleTemplate">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
<DataTemplate x:Key="EditTitleTemplate">
<TextBox x:Name="Fox"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding Path=Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource HighlightTextBoxStyle}">
</TextBox>
</DataTemplate>
</Window.Resources>
<DockPanel>
<TextBox DockPanel.Dock="Top" x:Name="Test" Text="{Binding Path=(FocusManager.FocusedElement).Name, ElementName=MyWindow}"
Style="{StaticResource HighlightTextBoxStyle}"/>
<toolkit:DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTemplateColumn Header="Templated Title"
CellTemplate="{StaticResource DefaultTitleTemplate}"
CellEditingTemplate="{StaticResource EditTitleTemplate}" />
<toolkit:DataGridTextColumn Header="Title" Binding="{Binding Path=Title}" />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
</DockPanel>
Missed updating the post with an answer...
The problem seems to be that for a custom data grid column (aka a DataGridTemplateColumn) the grid has no way of knowing the exact type of the editing control (which is specified via a DataTemplate and could be anything). For a DataGridTextColumn, the editing control type is known and hence the grid can find it and invoke a SelectAll() in it.
So to achieve the end-goal for a TemplateColumn, you need to provide an assist. I forgotten how I solved it the first time around.. but here is something that I searched-tweaked out today. Create a custom derivation of a TemplateColumn with an override of the PrepareCellForEdit method as shown below (Swap Textbox with your exact editing control).
public class MyCustomDataColumn : DataGridTemplateColumn
{
protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
{
var contentPresenter = editingElement as ContentPresenter;
var editingControl = FindVisualChild<TextBox>(contentPresenter);
if (editingControl == null)
return null;
editingControl.SelectAll();
return null;
}
private static childItem FindVisualChild<childItem>(DependencyObject obj)
}
Here's an implementation for FindVisualChild.
XAML:
<WPFTestBed:MyCustomDataColumn Header="CustomColumn"
CellTemplate="{StaticResource DefaultTitleTemplate}"
CellEditingTemplate="{StaticResource EditTitleTemplate}"/>
</DataGrid.Columns>
Lot of code for an annoying inconsistency.
I know this is way late but I took a different approach and creatively extended the TextBox class. I don't really like using the boolean to check if the text is already defined but the problem is that the selection events all fire before the text is set from the binding so SelectAll() doesn't have anything to select! This class is probably only useful as a editing template in something like a DataGridTemplateColumn. Every solution I found for this issue is pretty much a hack so I don't feel too bad about this one ... :)
class AutoSelectTextBox : TextBox
{
private bool _autoSelectAll= true;
protected override void OnInitialized(EventArgs e)
{
// This will cause the cursor to enter the text box ready to
// type even when there is no content.
Focus();
base.OnInitialized(e);
}
protected override OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
// This is here to handle the case of an empty text box. If
// omitted then the first character would be auto selected when
// the user starts typing.
_autoSelectAll = false;
base.OnKeyDown(e);
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (_autoSelectAll)
{
SelectAll();
Focus();
_autoSelectAll= false;
}
base.OnTextChanged(e);
}
}
Kinda VERY late...just putting this out here in case someone can use this
I had a similar need to DeSelect (or Select All) text in a DataGridTextColumn on editing
Just added the method to the PreparingCellForEdit event of the DataGrid
DataGrid.PreparingCellForEdit += DataGrid_PreparingCellForEdit;
Then assigned the (e.EditingElement as TextBox) and then set my options
private void DataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
var txtBox = e.EditingElement as TextBox;
txtBox.Select(txtBox.Text.Length, 0); //to DeSelect all and place cursor at end
txtBox.SelectAll(); // to selectall
}

DataTrigger not reevaluating after property changes

[Original]
I have a ListBox which has its ItemsSource (this is done in the code behind on as the window is created) databound to an ObservableCollection. The ListBox then has the following DataTemplate assigned against the items:
usercontrol.xaml
<ListBox x:Name="communicatorListPhoneControls"
ItemContainerStyle="{StaticResource templateForCalls}"/>
app.xaml
<Style x:Key="templateForCalls" TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate" Value="{StaticResource templateRinging}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=hasBeenAnswered}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource templateAnswered}"/>
</DataTrigger>
</Style.Triggers>
</Setter>
</Style>
When the ObservableCollection is updated with an object, this appears in the ListBox with the correct initial DataTemplate, however when the hasBeenAnswered property is set to true (when debugging i can see the collection is correct) the DataTrigger does not re-evaluate and then update the ListBox to use the correct DataTemplate.
I have implemented the INotifyPropertyChanged Event in my object, and if in the template is bound to a value, i can see the value update. Its just that the DataTrigger will not re-evaluate and change to the correct template.
I know the DataTrigger binding is correct because if i close the window and open it again, it will correctly apply the second datatemplate, because the hasBeenAnswered is set to true.
[edit 1]
Following on from comments made by Timores I've tried the following:
usercontrol.xaml
<ListBox x:Name="communicatorListPhoneControls"
ItemTemplate="{StaticResource communicatorCallTemplate}"/>`
app.xaml:
<DataTemplate x:Key="communicatorCallTemplate">
<Label x:Name="test">Not answered</Label>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=hasBeenAnswered}" Value="True">
<Setter TargetName="test" Property="Background" Value="Blue"/>
</DataTrigger>
</DataTemplate.Triggers>
</Label>
</DataTemplate>
What happens now is similar to the first example, when a call comes in the "Not answered" label shows (one per call that exists as this is a listbox - normally when the window loads there will be no calls), the call is then answered and the proptery hasBeenAnswered is set to true, yet the "Not Answered" remains the same. If i close the window, and re-open it again (with the active call still with the property hasBeenAnswered set to true) the background is then blue. So it would appear to me like the datatrigger is simply not being run, until the window is re-run.
What seems strange to me in the example is that you are using an ItemContainerStyle instead of an ItemTemplate.
ItemContainerStyle applies to the ListBoxItem that contains each element in your ItemsSource. The ListboxItem does not have an hasBeenAnswered property, so I don't see how the binding could work.
I suggest creating a DataTemplate for the data type in your list box and using triggers to make the same changes as in your templateAnswered style.
Edit: after OP used the suggestion of the ItemTemplate.
I tried to reproduce the example, and it works fine for me.
Here is my XAML (please disregard style, this is just an example):
Not answered
<ListBox x:Name="communicatorListPhoneControls"
ItemTemplate="{StaticResource communicatorCallTemplate}"/>
<Button Margin="0,20,0,0" Click="OnToggleAnswer" Content="Toggle answer status" />
</StackPanel>
And in the code-behind:
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
List<PhoneCall> lpc = new List<PhoneCall>()
{new PhoneCall(), new PhoneCall(), new PhoneCall(), new PhoneCall()};
communicatorListPhoneControls.ItemsSource = lpc;
}
private void OnToggleAnswer(object sender, RoutedEventArgs e) {
object o = communicatorListPhoneControls.SelectedItem;
if (o != null) {
PhoneCall pc = (PhoneCall) o;
pc.hasBeenAnswered = ! pc.hasBeenAnswered;
}
}
}
public class PhoneCall : INotifyPropertyChanged {
private bool _answered;
public bool hasBeenAnswered {
get { return _answered; }
set {
if (_answered != value) {
_answered = value;
FirePropertyChanged("hasBeenAnswered");
}
}
}
private void FirePropertyChanged(string propName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Could you try to reproduce this and compare with your code ?
Note: the smallest error in the property name given to PropertyChanged could explain your behaviour. The trigger could be based on the right property, but the notification could have a misspelled name.

Create ToolTip on Silverlight DataGridTextColumn that is different from the bound cell contents

I have a DataGridTextColumn in a Silverlight 4 DataGrid and I want to set a ToolTip value on the column that is different from the bound value.
I know I can do this with a templated column quite easily - but it adds a huge amount of extra XAML and makes it cumbersome to read and maintain.
This works, but is a lot of extra code - especially if ever need to change the template
<data:DataGridTemplateColumn Header="Name" Width="*">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextTrimming="WordEllipsis" Text="{Binding FullName}"
ToolTipService.ToolTip="{Binding Email}" Width="Auto" Margin="5" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
I'd like to find a nice way to do this with either a Style or inherited class. Like I said my main goal is to reduce bloat in the XAML for something so trivial as a tooltip in the best possible way.
There are a few similar stackoverflow questions with solutions like this and this, but they both show the same tooltip value as the contents of the cell (for instance when it overflows). While this is often what you want - I'm trying to show a different tooltip to the cell's contents.
I did find some sample code for an inherited class (scroll to end), which i tried to modify but got stuck becasue my XAML knowledge isn't up to par and I don't want to spend all night on this! This particular example appears like it works, but it looks like quite a hack and I think trying to modify it to work with two dependency properties is going to be an even bigger one.
PS. I would expect that a well written subclass would make it easy for me to bind other properties such as TextTrimming also.
Try to put this style in your resource dictionary:
<Style TargetType="{x:Type data:DataGridCell}">
<Setter Property="ToolTip" Value="{Binding EMail}"/>
</Style>
To enable binding to any property, as you requested in the comment, you need to get creative. What I did was create two attached properties ToolTipBinding and IsToolTipBindingEnabled. The ToolTipBinding is set on the column to determine the tooltip, similar to the Binding property that determines the content of the cell, and the IsToolTipBindingEnabled is set to true on the DataGridCell object using a style similar to the one I mentioned above.
Then, I wrote code so that when the cell is loaded, the binding from its parent column is applied to its ToolTip property.
Here's the extension class:
public class DGExtensions
{
public static object GetToolTipBinding(DependencyObject obj)
{
return obj.GetValue(ToolTipBindingProperty);
}
public static void SetToolTipBinding(DependencyObject obj, object value)
{
obj.SetValue(ToolTipBindingProperty, value);
}
public static readonly DependencyProperty ToolTipBindingProperty =
DependencyProperty.RegisterAttached(
"ToolTipBinding",
typeof(object),
typeof(DGExtensions),
new FrameworkPropertyMetadata(null));
public static bool GetIsToolTipBindingEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsToolTipBindingEnabled);
}
public static void SetIsToolTipBindingEnabled(
DependencyObject obj,
bool value)
{
obj.SetValue(IsToolTipBindingEnabled, value);
}
public static readonly DependencyProperty IsToolTipBindingEnabled =
DependencyProperty.RegisterAttached(
"IsToolTipBindingEnabled",
typeof(bool),
typeof(DGExtensions),
new UIPropertyMetadata(false, OnIsToolTipBindingEnabledChanged));
public static void OnIsToolTipBindingEnabledChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
DataGridCell cell = d as DataGridCell;
if (cell == null) return;
bool nv = (bool)e.NewValue, ov = (bool)e.OldValue;
// Act only on an actual change of property value.
if (nv == ov) return;
if (nv)
cell.Loaded += new RoutedEventHandler(cell_Loaded);
else
cell.Loaded -= cell_Loaded;
}
static void cell_Loaded(object sender, RoutedEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell == null) return;
var binding = BindingOperations.GetBinding(
cell.Column, ToolTipBindingProperty);
if (binding == null) return;
cell.SetBinding(DataGridCell.ToolTipProperty, binding);
// This only gets called once, so remove the strong reference.
cell.Loaded -= cell_Loaded;
}
}
Example XAML usage:
<Window.Resources>
<Style TargetType="{x:Type tk:DataGridCell}">
<Setter
Property="dge:DGExtensions.IsToolTipBindingEnabled"
Value="True"/>
</Style>
</Window.Resources>
<Grid>
<tk:DataGrid ItemsSource="{Binding TheList}" AutoGenerateColumns="False">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn
Header="PropA"
Binding="{Binding PropA}"
dge:DGExtensions.ToolTipBinding="{Binding PropB}"/>
<tk:DataGridTextColumn
Header="PropB"
Binding="{Binding PropB}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
</Grid>

Resources