Initial Focus and Select All behavior - wpf

I have a user control that is nested inside a window that is acting as a shell for a dialog display. I ignore focus in the shell window, and in the hosted user control I use the FocusManager to set the initial focus to a named element (a textbox) as shown below.
This works, setting the cursor at the beginning of the named textbox; however I want all text to be selected.
The TextBoxSelectionBehavior class (below) usually does exactly that, but not in this case. Is there an easy xaml fix to get the text in the named textbox selected on initial focus?
Cheers,
Berryl
TextBox Selection Behavior
// in app startup
TextBoxSelectionBehavior.RegisterTextboxSelectionBehavior();
/// <summary>
/// Helper to select all text in the text box on entry
/// </summary>
public static class TextBoxSelectionBehavior
{
public static void RegisterTextboxSelectionBehavior()
{
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotFocusEvent, new RoutedEventHandler(OnTextBox_GotFocus));
}
private static void OnTextBox_GotFocus(object sender, RoutedEventArgs e)
{
var tb = (sender as TextBox);
if (tb != null)
tb.SelectAll();
}
}
The hosted UserControl
<UserControl
<DockPanel KeyboardNavigation.TabNavigation="Local"
FocusManager.FocusedElement="{Binding ElementName=tbLastName}" >
<TextBox x:Name="tbLastName" ... />
stop gap solution
Per comments with Rachel below, I ditched the FocusManger in favor of some code behind:
tbLastName.Loaded += (sender, e) => tbLastName.Focus();
Still would love a declarative approach for a simple and common chore though...

I usually use an AttachedProperty to make TextBoxes highlight their text on focus. It is used like
<TextBox local:HighlightTextOnFocus="True" />
Code for attached property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus",
typeof(bool), typeof(TextBoxProperties),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
[AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool)obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (sender != null)
{
if ((bool)e.NewValue)
{
sender.GotKeyboardFocus += OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
sender.GotKeyboardFocus -= OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
}
private static void OnKeyboardFocusSelectText(
object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
private static void OnMouseLeftButtonDownSetFocus(
object sender, MouseButtonEventArgs e)
{
TextBox tb = FindAncestor<TextBox>((DependencyObject)e.OriginalSource);
if (tb == null)
return;
if (!tb.IsKeyboardFocusWithin)
{
tb.Focus();
e.Handled = true;
}
}
static T FindAncestor<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;
}
Edit
Based on comments below, what about just getting rid of the FocusManager.FocusedElement and setting tb.Focus() and tb.SelectAll() in the Loaded event of your TextBox?

As stated above, you can add an event handler for the Loaded event to set focus and select all text:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
base.DataContext = new Person { FirstName = "Joe", LastName = "Smith" };
base.Loaded += delegate
{
this._firstNameTextBox.Focus();
this._firstNameTextBox.SelectAll();
};
}
}

Related

How to load different images on different events of a button control using MVVM?

Using MVVM architecture, I want to load an image on a button.
As I have similar button in many places I want to make this as common.
1) Initially when the form is launched an image should be loaded.
2) When "MouseEnter" event then cursor image should change to hand.
3) When "MouseLeave" event then cursor image should change to Arrow.
4) When "MouseLeftButtonDown" then an image should be loaded.
5) When "MouseLeftButtonUp" then an image should be loaded.
Like this, I want to load different images on different events and this button needs to be used in
many other dialogs. How can design it such that it can be as a common resource for all.
Could any one please help me to solve this issue.
I am very new to WPF. Please be kind with your answers.
You could use attached properties for this. If you need different events, then you will have to add more attached properties.
Note that #2 and #3 could simply be Cursor="Hand" on the button. There is nothing to implement for #3 - the cursor will change on it's own when you leave the button. I added it as an attached property just for fun. It is completely useless and really just duplicates Cursor="Hand".
disk_blue.png, disk_green.png and disk_yellow.png were added to the root of the application by just dragging and dropping them onto the project in Visual Studio. The build action is "Resource".
Here is a complete demo app:
XAML:
<Window x:Class="WpfApplication56.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication56"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:VM InitialImage="disk_blue.png" MouseLeftButtonDownImage="disk_green.png" MouseLeftButtonUpImage="disk_yellow.png" />
</Window.DataContext>
<Grid>
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Padding="20"
local:MyButton.MouseEnterCursor="Hand"
local:MyButton.InitialImage="{Binding InitialImage}"
local:MyButton.MouseLeftButtonDown="{Binding MouseLeftButtonDownImage}"
local:MyButton.MouseLeftButtonUp="{Binding MouseLeftButtonUpImage}"
/>
</Grid>
</Window>
CS:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
namespace WpfApplication56
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VM // I didn't implement INotifyPropertyChanged because it doesn't matter for this question
{
public Uri InitialImage { get; set; }
public Uri MouseLeftButtonDownImage { get; set; }
public Uri MouseLeftButtonUpImage { get; set; }
}
public class MyButton
{
// Attached Properties
#region MouseEnterCursorProperty
public static Cursor GetMouseEnterCursor(DependencyObject obj)
{
return (Cursor)obj.GetValue(MouseEnterCursorProperty);
}
public static void SetMouseEnterCursor(DependencyObject obj, Cursor value)
{
obj.SetValue(MouseEnterCursorProperty, value);
}
public static readonly DependencyProperty MouseEnterCursorProperty = DependencyProperty.RegisterAttached("MouseEnterCursor", typeof(Cursor), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region InitialImageProperty
public static Uri GetInitialImage(DependencyObject obj)
{
return (Uri)obj.GetValue(InitialImageProperty);
}
public static void SetInitialImage(DependencyObject obj, Uri value)
{
obj.SetValue(InitialImageProperty, value);
}
public static readonly DependencyProperty InitialImageProperty = DependencyProperty.RegisterAttached("InitialImage", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region MouseLeftButtonDownProperty
public static Uri GetMouseLeftButtonDown(DependencyObject obj)
{
return (Uri)obj.GetValue(MouseLeftButtonDownProperty);
}
public static void SetMouseLeftButtonDown(DependencyObject obj, Uri value)
{
obj.SetValue(MouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty MouseLeftButtonDownProperty = DependencyProperty.RegisterAttached("MouseLeftButtonDown", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
#region MouseLeftButtonUpProperty
public static Uri GetMouseLeftButtonUp(DependencyObject obj)
{
return (Uri)obj.GetValue(MouseLeftButtonUpProperty);
}
public static void SetMouseLeftButtonUp(DependencyObject obj, Uri value)
{
obj.SetValue(MouseLeftButtonUpProperty, value);
}
public static readonly DependencyProperty MouseLeftButtonUpProperty = DependencyProperty.RegisterAttached("MouseLeftButtonUp", typeof(Uri), typeof(MyButton), new PropertyMetadata(null, OnPropertyChanged));
#endregion
// Called when the value for an attached property has changed
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Button b = d as Button;
if (b == null)
return;
if (e.Property.Equals(MouseEnterCursorProperty))
{
b.MouseEnter -= MouseEnter; // In case the property changes more than once we don't want to hook it more than once
b.MouseEnter += MouseEnter;
}
else if (e.Property.Equals(InitialImageProperty))
{
SetButtonImage(b, GetInitialImage(b));
}
else if (e.Property.Equals(MouseLeftButtonDownProperty))
{
// Had to use Preview because non-Preview never fired
b.PreviewMouseLeftButtonDown -= MouseLeftButtonDown;
b.PreviewMouseLeftButtonDown += MouseLeftButtonDown;
}
else if (e.Property.Equals(MouseLeftButtonUpProperty))
{
// Had to use Preview because non-Preview never fired
b.PreviewMouseLeftButtonUp -= MouseLeftButtonUp;
b.PreviewMouseLeftButtonUp += MouseLeftButtonUp;
}
}
private static void MouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
b.Cursor = GetMouseEnterCursor(b);
e.Handled = false;
}
private static void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
SetButtonImage(b, GetMouseLeftButtonDown(b));
e.Handled = false;
}
private static void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Button b = sender as Button;
if (b == null)
return;
SetButtonImage(b, GetMouseLeftButtonUp(b));
e.Handled = false;
}
private static void SetButtonImage(Button b, Uri uri)
{
if (b == null || uri == null)
return;
var img = b.Content as Image;
if (img == null)
img = new Image();
BitmapImage bm = new BitmapImage();
bm.BeginInit();
bm.UriSource = uri;
bm.EndInit();
img.Source = bm;
b.Content = img;
}
}
}
Screenshots
Initial (mouse was over it to show cursor change but cursor isn't in the image):
LeftMouseButtonDown:
LeftMouseButtonUp:

WPF MVVM design question

In my View I have a TreeView with a event "TreeView_MouseLeftButtonDown". If it fires it proofs if the mouse clicked on a TreeViewItem. If not it deselects the last TreeViewItem.
My question is, should i do this in the code-behind or call a static methode in the viewmodel-class? How would you solve this?
The Methode:
private void treeView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender != null)
{
var treeView = sender as TreeView;
if (treeView != null && treeView.SelectedItem != null)
TreeViewHelper.ReturnTreeViewItem(ref treeView, (XmlNode)treeView.SelectedItem).IsSelected = false;
}
}
XAML:
<TreeView ... KeyDown="TreeView_KeyDown"
MouseLeftButtonDown="TreeView_MouseLeftButtonDown"
SelectedItemChanged="TreeView_SelectedItemChanged" />
You are trying to add a behaviour to the TreeView.
The way I would implement this would be using Attached Properties. I would create an attached property called VerifiesLeftClick or similar and implement the logic in there. This way you do not need an event in the code behind.
See here for samples.
I made for you solution using attached behaviors which were pretty well described here Introduction to Attached Behaviors in WPF by Josh Smith
My solution:
public static class TreeViewBehavior
{
public static bool GetIsResetMouseLeftButtonDown(TreeView treeView)
{
return (bool)treeView.GetValue(IsResetMouseLeftButtonDownProperty);
}
public static void SetIsResetMouseLeftButtonDown(TreeView treeViewItem, bool value)
{
treeViewItem.SetValue(IsResetMouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty IsResetMouseLeftButtonDownProperty =
DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDown", typeof(bool), typeof(TreeViewBehavior),
new UIPropertyMetadata(false, OnIsMouseLeftButtonDownChanged));
static void OnIsMouseLeftButtonDownChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TreeView item = depObj as TreeView;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
{
item.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
else
{
item.MouseLeftButtonDown -= OnMouseLeftButtonDown;
}
}
static void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
{
var tempItem = e.Source as TreeViewItem;
if (tempItem != null && tempItem.IsSelected == false)
{
tempItem.IsSelected = true;
}
else
{
var tree = e.Source as TreeView;
if (tree != null && tree.SelectedItem != null)
{
var selItem = (tree.SelectedItem as TreeViewItem);
if (selItem != null)
{
selItem.IsSelected = false;
}
}
}
}
}
and then in View you should add this:
<TreeView local:TreeViewBehavior.IsResetMouseLeftButtonDown="True">
I hope my solution do what you are trying to achieve.

Setting focus to a control in WPF using MVVM

I want keyboard focus to be set to a TextBox when I click a Button on my view. I don't want to use any codebehind, so wondered if anyone had written an attached property or similar solution?
Try this out:
public static class FocusBehavior
{
public static readonly DependencyProperty ClickKeyboardFocusTargetProperty =
DependencyProperty.RegisterAttached("ClickKeyboardFocusTarget", typeof(IInputElement), typeof(FocusBehavior),
new PropertyMetadata(OnClickKeyboardFocusTargetChanged));
public static IInputElement GetClickKeyboardFocusTarget(DependencyObject obj)
{
return (IInputElement)obj.GetValue(ClickKeyboardFocusTargetProperty);
}
public static void SetClickKeyboardFocusTarget(DependencyObject obj, IInputElement value)
{
obj.SetValue(ClickKeyboardFocusTargetProperty, value);
}
private static void OnClickKeyboardFocusTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var button = sender as ButtonBase;
if (button == null)
return;
if (e.OldValue == null && e.NewValue != null)
button.Click += OnButtonClick;
else if (e.OldValue != null && e.NewValue == null)
button.Click -= OnButtonClick;
}
private static void OnButtonClick(object sender, RoutedEventArgs e)
{
var target = GetKeyboardClickFocusTarget((ButtonBase)sender);
Keyboard.Focus(target);
}
}
Then to use it,
<TextBox x:Name="TargetTextBox"/>
<Button b:FocusBehavior.ClickKeyboardFocusTarget="{Binding ElementName=TargetTextBox}"/>

What is the proper way to handle multiple datagrids in a tab control so that cells leave edit mode when the tabs are changed?

In wpf I setup a tab control that binds to a collection of objects each object has a data template with a data grid presenting the data. If I select a particular cell and put it into edit mode, leaving the grid by going to another tab this will cause the exception below to be thrown on returning the datagrid:
'DeferRefresh' is not allowed during an AddNew or EditItem transaction.
It appears that the cell never left edit mode. Is there an easy way to take the cell out of edit mode, or is something else going on here?
Update: It looks like if I do not bind the tab control to the data source, but instead explicitly define each tab and then bind each item in the data source to a content control this problem goes away. This is not really a great solution, so I would still like to know how to bind the collection directly to the tab control.
Update: So what I have actually done for my own solution is to use a ListView and a content control in place of a tab control. I use a style to make the list view look tab like. The view model exposes a set of child view models and allows the user to select one via the list view. The content control then presents the selected view model and each view model has an associated data template which contains the data grid. With this setup switching between view models while in edit mode on the grid will properly end edit mode and save the data.
Here is the xaml for setting this up:
<ListView ItemTemplate="{StaticResource MakeItemsLookLikeTabs}"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding Selected}"
Style="{StaticResource MakeItLookLikeATabControl}"/>
<ContentControl Content="{Binding Selected}">
I'll accept Phil's answer as that should work also, but for me the solution above seems like it will be more portable between projects.
I implemented a behavior for the DataGrid based on code I found in this thread.
Usage:<DataGrid local:DataGridCommitEditBehavior.CommitOnLostFocus="True" />
Code:
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
/// <summary>
/// Provides an ugly hack to prevent a bug in the data grid.
/// https://connect.microsoft.com/VisualStudio/feedback/details/532494/wpf-datagrid-and-tabcontrol-deferrefresh-exception
/// </summary>
public class DataGridCommitEditBehavior
{
public static readonly DependencyProperty CommitOnLostFocusProperty =
DependencyProperty.RegisterAttached(
"CommitOnLostFocus",
typeof(bool),
typeof(DataGridCommitEditBehavior),
new UIPropertyMetadata(false, OnCommitOnLostFocusChanged));
/// <summary>
/// A hack to find the data grid in the event handler of the tab control.
/// </summary>
private static readonly Dictionary<TabPanel, DataGrid> ControlMap = new Dictionary<TabPanel, DataGrid>();
public static bool GetCommitOnLostFocus(DataGrid datagrid)
{
return (bool)datagrid.GetValue(CommitOnLostFocusProperty);
}
public static void SetCommitOnLostFocus(DataGrid datagrid, bool value)
{
datagrid.SetValue(CommitOnLostFocusProperty, value);
}
private static void CommitEdit(DataGrid dataGrid)
{
dataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
}
private static DataGrid GetParentDatagrid(UIElement element)
{
UIElement childElement; // element from which to start the tree navigation, looking for a Datagrid parent
if (element is ComboBoxItem)
{
// Since ComboBoxItem.Parent is null, we must pass through ItemsPresenter in order to get the parent ComboBox
var parentItemsPresenter = VisualTreeFinder.FindParentControl<ItemsPresenter>(element as ComboBoxItem);
var combobox = parentItemsPresenter.TemplatedParent as ComboBox;
childElement = combobox;
}
else
{
childElement = element;
}
var parentDatagrid = VisualTreeFinder.FindParentControl<DataGrid>(childElement);
return parentDatagrid;
}
private static TabPanel GetTabPanel(TabControl tabControl)
{
return
(TabPanel)
tabControl.GetType().InvokeMember(
"ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null,
tabControl,
null);
}
private static void OnCommitOnLostFocusChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var dataGrid = depObj as DataGrid;
if (dataGrid == null)
{
return;
}
if (e.NewValue is bool == false)
{
return;
}
var parentTabControl = VisualTreeFinder.FindParentControl<TabControl>(dataGrid);
var tabPanel = GetTabPanel(parentTabControl);
if (tabPanel != null)
{
ControlMap[tabPanel] = dataGrid;
}
if ((bool)e.NewValue)
{
// Attach event handlers
if (parentTabControl != null)
{
tabPanel.PreviewMouseLeftButtonDown += OnParentTabControlPreviewMouseLeftButtonDown;
}
dataGrid.LostKeyboardFocus += OnDataGridLostFocus;
dataGrid.DataContextChanged += OnDataGridDataContextChanged;
dataGrid.IsVisibleChanged += OnDataGridIsVisibleChanged;
}
else
{
// Detach event handlers
if (parentTabControl != null)
{
tabPanel.PreviewMouseLeftButtonDown -= OnParentTabControlPreviewMouseLeftButtonDown;
}
dataGrid.LostKeyboardFocus -= OnDataGridLostFocus;
dataGrid.DataContextChanged -= OnDataGridDataContextChanged;
dataGrid.IsVisibleChanged -= OnDataGridIsVisibleChanged;
}
}
private static void OnDataGridDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dataGrid = (DataGrid)sender;
CommitEdit(dataGrid);
}
private static void OnDataGridIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var senderDatagrid = (DataGrid)sender;
if ((bool)e.NewValue == false)
{
CommitEdit(senderDatagrid);
}
}
private static void OnDataGridLostFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var dataGrid = (DataGrid)sender;
var focusedElement = Keyboard.FocusedElement as UIElement;
if (focusedElement == null)
{
return;
}
var focusedDatagrid = GetParentDatagrid(focusedElement);
// Let's see if the new focused element is inside a datagrid
if (focusedDatagrid == dataGrid)
{
// If the new focused element is inside the same datagrid, then we don't need to do anything;
// this happens, for instance, when we enter in edit-mode: the DataGrid element loses keyboard-focus,
// which passes to the selected DataGridCell child
return;
}
CommitEdit(dataGrid);
}
private static void OnParentTabControlPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var dataGrid = ControlMap[(TabPanel)sender];
CommitEdit(dataGrid);
}
}
public static class VisualTreeFinder
{
/// <summary>
/// Find a specific parent object type in the visual tree
/// </summary>
public static T FindParentControl<T>(DependencyObject outerDepObj) where T : DependencyObject
{
var dObj = VisualTreeHelper.GetParent(outerDepObj);
if (dObj == null)
{
return null;
}
if (dObj is T)
{
return dObj as T;
}
while ((dObj = VisualTreeHelper.GetParent(dObj)) != null)
{
if (dObj is T)
{
return dObj as T;
}
}
return null;
}
}
I have managed to work around this issue by detecting when the user clicks on a TabItem and then committing edits on visible DataGrid in the TabControl. I'm assuming the user will expect their changes to still be there when they click back.
Code snippet:
// PreviewMouseDown event handler on the TabControl
private void TabControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (IsUnderTabHeader(e.OriginalSource as DependencyObject))
CommitTables(yourTabControl);
}
private bool IsUnderTabHeader(DependencyObject control)
{
if (control is TabItem)
return true;
DependencyObject parent = VisualTreeHelper.GetParent(control);
if (parent == null)
return false;
return IsUnderTabHeader(parent);
}
private void CommitTables(DependencyObject control)
{
if (control is DataGrid)
{
DataGrid grid = control as DataGrid;
grid.CommitEdit(DataGridEditingUnit.Row, true);
return;
}
int childrenCount = VisualTreeHelper.GetChildrenCount(control);
for (int childIndex = 0; childIndex < childrenCount; childIndex++)
CommitTables(VisualTreeHelper.GetChild(control, childIndex));
}
This is in the code behind.
This bug is solved in the .NET Framework 4.5. You can download it at this link.
What I think you should do is pretty close to what #myermian said.
There is an event called CellEditEnding end this event would allow you to intercept and make the decision to drop the unwanted row.
private void dataGrid1_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
DataGrid grid = (DataGrid)sender;
TextBox cell = (TextBox)e.EditingElement;
if(String.IsNullOrEmpty(cell.Text) && e.EditAction == DataGridEditAction.Commit)
{
grid.CancelEdit(DataGridEditingUnit.Row);
e.Cancel = true;
}
}

WPF DialogResult declaratively?

In WinForms we could specify DialogResult for buttons. In WPF we can declare in XAML only Cancel button:
<Button Content="Cancel" IsCancel="True" />
For others we need to catch ButtonClick and write code like that:
private void Button_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
I am using MVVM, so I have only XAML code for windows. But for modal windows I need to write such code and I don't like this. Is there a more elegant way to do such things in WPF?
You can do this with an attached behavior to keep your MVVM clean. The C# code for your attached behavior might look something like so:
public static class DialogBehaviors
{
private static void OnClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var parent = VisualTreeHelper.GetParent(button);
while (parent != null && !(parent is Window))
{
parent = VisualTreeHelper.GetParent(parent);
}
if (parent != null)
{
((Window)parent).DialogResult = true;
}
}
private static void IsAcceptChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var button = (Button)obj;
var enabled = (bool)e.NewValue;
if (button != null)
{
if (enabled)
{
button.Click += OnClick;
}
else
{
button.Click -= OnClick;
}
}
}
public static readonly DependencyProperty IsAcceptProperty =
DependencyProperty.RegisterAttached(
name: "IsAccept",
propertyType: typeof(bool),
ownerType: typeof(Button),
defaultMetadata: new UIPropertyMetadata(
defaultValue: false,
propertyChangedCallback: IsAcceptChanged));
public static bool GetIsAccept(DependencyObject obj)
{
return (bool)obj.GetValue(IsAcceptProperty);
}
public static void SetIsAccept(DependencyObject obj, bool value)
{
obj.SetValue(IsAcceptProperty, value);
}
}
You can use the property in XAML with the code below:
<Button local:IsAccept="True">OK</Button>
An alternative way is to use Popup Control
Try this tutorial.

Resources