I have a UserControl with a number of StackPanel's. I like to hide specific panels depending on the user action. A StackPanel which is visible on startup gives me a number of working buttons. The buttons have click events in the code behind file. After collapsing the panel and then making it visible again the buttons no longer work. Here is a part of my UserControl:
<StackPanel x:Name="buttonPanel" Orientation="Horizontal">
<Button x:Name="ReMindNodeNotes" Content=""
FontFamily="Segoe UI Symbol" FontSize="14" Foreground="#FF292323"
HorizontalAlignment="Left" BorderThickness="1" Padding="0"
UseLayoutRounding="True" Click="NoteClicked" />
<Button x:Name="ReMindNodeRemove" Content=""
FontFamily="Segoe UI Symbol" FontSize="14" Foreground="#FF292323"
HorizontalAlignment="Left" BorderThickness="1" Padding="0"
UseLayoutRounding="True" Click="RemoveClicked" />
</StackPanel>
And here is the code (for now just some text):
private void NoteClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("NoteClicked...");
}
private void RemoveClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("RemoveClicked...");
}
I have been looking for a solution the last two days. No luck so far. Who can help...?
THX Peter
Follow up 1...
Here is the code for collapsing the panel:
private void MoreClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(this.nodeName);
this.buttonPanel.Visibility =
this.buttonPanel.Visibility ==
Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
It works if the buttonPanel has focus. If the focus is on another panel it does not. Furthermore, what I probably should have mentioned... is that users can create multiple instances of the user control.
THX
Follow up 2...
I continue working on a solution of course... ;-) and I found a solution, which however is not the solution I want. Let me explain.
Users can interactively create multiple instances of the user control mentioned before. When a new instance is created, that instance gets focus. Now every instance has its own set of buttons which are on a stackpanel. When the focus goes to another instance I want the panel of the previous instance to collapse. The focus should then be set to the new (or selected existing) instance.
When I do this manually, it works! When I try to achieve this through the GotFocus and LostFocus events however, it does not. Here is the code for the manual solution (which works):
private void MoreClicked(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility =
this.buttonPanel.Visibility ==
Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
Here are the LostFocus and GotFocus events:
private void NodeGotFocus(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility = Visibility.Visible;
}
private void NodeLostFocus(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
I really appreciate your help! THX again...
Thanks for your sample morincer. The problem however is a little more complex. Let me try to explain the solution which I found after some more research. Maybe other developers can benefit from it as well.
I added the GotFocus and LostFocus events to my userconctrol. If I click somewhere inside the usercontrol the focus changes every time. Strange as these events are only defined on the usercontrol itself and not it's children. I have several buttons and a textbox inside the usercontrol and when I for example click on one of the buttons of the usercontrol that has focus the LostFocus and GotFocus events are fired for usercontrol anyway.
The most important event for me in this case is the LostFocus event. When the usercontrol looses focus - for example to another control - I want the button panel to disappear. Since the LostFocus event fires every time a object inside the usercontrol is touched, I cannot distinguish between the situation in which I want to hide and show the buttons.
I got a little closer to a solution by changing the LostFocus event as follows:
private void LostFocus(object sender, RoutedEventArgs e)
{
Object fo = FocusManager.GetFocusedElement();
if (fo.GetType().ToString().Contains("TextBox") ||
fo.GetType().ToString().Contains("ScrollViewer"))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
This covers most of the situations. When the cursor is positioned in the TextBox the button panel is closed. The button panel is also closed when the user clicks on the background. This seems to be a ScrollViewer (found through debugging the code). Can anyone explain this...?
The situation which is not covered however, is when a user clicks on another usercontrol. It does of course when the user clicks on the TextBox (see the code) but not when the user clicks on a button. I tried to compare sender and FocusManager.GetFocusedElement(). Problem is that the sender returns the usercontrol (which is what I am looking for) but the FocusManager.GetFocusedElement() returns the button that was pressed. Now I could ask for it's parent which is a border then ask for the borders parent which is a stack panel and so on until I arrive at the usercontrol. A code behind file however was introduced with the idea to split design and logic while this solution would tie them together again. If I would change the XAML I would have to change the logic as well. Doesn't seem to be the right solution to me.
I found a solotion by giving every usercontrol a unique name in the constructor. I then give all the buttons unique names as well (I don't use them in my code anyway) starting with the name of the usercontrol. This then gives me the possibility to compare names at runtime and determine whether the focus has changed to another instance of the usercontrol. Here is the code:
private void NodeLostFocus(object sender, RoutedEventArgs e)
{
Object fo = FocusManager.GetFocusedElement();
if (fo.GetType().ToString().Contains("ScrollViewer"))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
else if (fo.GetType().ToString().Contains("TextBox"))
{
if (!((TextBox)fo).Name.Contains(this.nodeName))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
else if (fo.GetType().ToString().Contains("Button"))
{
if (!((Button)fo).Name.Contains(this.nodeName))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
}
Now this works! But…I don't like the solution. I am depending on names instead of a good architecture. Does anyone hove an idea how to compare the actual sender with the usercontrol that is the parent of the button pressed (FocusManager.GetFocusedElement())? Or any other solution that relies on good programming?
THX again
I have seen a few posts addressing how to remove an UserControl that has been added during runtime, but my problem is a little different. I have a UserControl that consists of an image with a small "x" button on the top right corner that is used to remove itself (the UserControl) from its parent canvas. Also to note is that the UserControl is added during runtime when the user doubleclicks on a ListboxItem. I have a Click event handler for the top right corner button but this code is not running at all. I know this because I have a breakpoint in this code which is not reached when I click the button.
So,
Why isn't the click event of the remove button being handled?
Maybe there is a better way to implement this. Please advise.
Here's the code used for adding it:
private void MyListBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.OriginalSource.ToString() == "System.Windows.Controls.Border" || e.OriginalSource.ToString() == "System.Windows.Controls.Image" || e.OriginalSource.ToString() == "System.Windows.Controls.TextBlock")
{
Expression.Blend.SampleData.MyCollection.Dataset lbi = ((sender as ListBox).SelectedItem as Expression.Blend.SampleData.MyCollection.Dataset);
var new_usercontrol = new MyUserControl();
new_usercontrol.MyImageSourceProperty = lbi.Image;
MyCanvas.Children.Add(new_usercontrol);
Canvas.SetLeft(new_usercontrol, 100);
Canvas.SetTop(new_usercontrol, 100);
Canvas.SetZIndex(new_usercontrol, 100);
}
}
The following is the cs code for the UserControl:
public partial class ModuleElement : UserControl
{
public ImageSource MyProperty
{
get { return (ImageSource)this.image.Source; }
set { this.image.Source = value; }
}
public ModuleElement()
{
this.InitializeComponent();
}
private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
((Canvas)this.Parent).Children.Remove(this);
}
}
The XAML:
<Grid x:Name="LayoutRoot">
<Image x:Name="image" />
<Button x:Name="RemoveButton" Content="X" HorizontalAlignment="Right" Height="17.834" Margin="0" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="RemoveButton_Click">
</Button>
</Grid>
Thanks in advance,
Bryan
So I tried your code here exactly except for some name changes and could not reproduce your issue. In my personal experience your issue here has to be that for some reason the event for the click isn't subscribed to properly. For this I would go into designer for the user control, wipe out the current event for the button and double click in the designer event textbox such that VS or Blend generates all the code necessary for a proper subscription.
I have created a sample based on your code here. Feel free to pull it down and take a look to see if you can find any inconsistencies.
As far as a better way to implement this, check out the good old MVVM pattern and the MVVM Light Toolkit. With this you can have a central ViewModel class that will handle all of your button commands and binding without code behind.
I'm using the Microsoft RibbonControl on a UserControl (it needs to be so we can host it on a stub form to host the WPF in our MDI system). Sadly, the title of the Ribbon displays Top/Left in the ribbon's header, and it looks ridiculous. How do I get at that sucker?
I am working on just about the same thing right now. I solved it by using a datatemplate for the ribbon title:
<r:Ribbon.TitleTemplate>
<DataTemplate>
<TextBlock Text="Put you title here" Margin="3,3,0,0"></TextBlock>
</DataTemplate>
</r:Ribbon.TitleTemplate>
If the ribbon is used in a RibbonWindow, you probably also want to add a glow to the title text to be able to read it properly when placed over a dark background. In that case, add this XAML inside the TextBlock:
<TextBlock.BitmapEffect>
<OuterGlowBitmapEffect GlowColor="White" Opacity="0.7" GlowSize="10"/>
</TextBlock.BitmapEffect>
There is one more problem with the Ribbon when used within a RibbonWindow; the title text will either be placed correctly when window state is Normal or when window is maximized. To solve this I bound the TextBlock Margin to a property in the codebind:
public Thickness TitleMargin
{
get { return this.WindowState == WindowState.Maximized ? new Thickness(0, 3, 0, 0) : new Thickness(0); }
}
To get this working, you also need to fire a PropertyChanged event each time the window state changes:
protected override void OnStateChanged(EventArgs e)
{
OnPropertyChanged("TitleMargin");
base.OnStateChanged(e);
}
I need to dynamically set the contents within the template of a DataGrid based on information in an external settings file. That settings file specifies which data fields should display in the DataGrid. The administrator of the application can edit the settings to change the fields to display. I cannot hard-code the fields to display.
I can easily add the columns (DataGridTextColumn's) to the DataGrid at runtime. I set a binding to a field in the item source based on the settings, and that displays fine.
Now I need to display details when the user clicks a row. I set up a RowDetailsTemplate with DataTemplate, and added a Grid (or a StackPanel) inside to format the details. If I add to the markup the TextBlocks with bindings to fields, it displays the details just fine.
But how can I set the content of the Grid/StackPanel in the details template programmatically? The Grid/StackPanel controls are null if I try to reference them by name on startup (e.g., in the page Loaded event). I have tried using the Loaded event on the Grid/StackPanel to add the details. That code runs and appears to add the content to the Grid/StackPanel, but nothing actually appears when I click the row. I'm guessing that the problem is that the template/Grid is already loaded and ignores the changes I'm making.
Here's a sample of the code I'm using in the handler for the Loaded event. Even if I do something as simple as this, the details pane doesn't appear when clicking on the row.
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border Background="LightBlue" >
<StackPanel x:Name="resultsDetailsPanel"
Orientation="Vertical"
Loaded="resultsDetailsPanel_Loaded">
</StackPanel>
</Border>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
private void resultsDetailsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel detailsPanel = sender as StackPanel;
TextBlock fieldNameTextBlock = new TextBlock();
fieldNameTextBlock.Text = "TESTING";
detailsPanel.Children.Add(fieldNameTextBlock);
_resultsGridLoaded = true;
}
I actually tried your code and is working. Two things you should check:
Is your _resultsGridLoaded variable initialized as false?
Did you set RowDetailsVisibilityMode="VisibleWhenSelected" on your DataGrid?
UPDATE: For some reason is not working anymore. But I did found two ways you can fix it:
Remove the resultsGridLoaded logic.
If you need that logic, you can add a handler for the SelectionChanged event on the DataGrid, in there you can set the _resultsGridLoaded variable to false so the new StackPanel gets its content added correctly:
And the code behind:
private void resultsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel pane = (StackPanel)sender;
TextBlock newChild = new TextBlock()
{
Text = "New text"
};
pane.Children.Add(newChild);
_resultsGridLoaded = true;
}
private void grid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_resultsGridLoaded = false;
}
Hope this helps
In my WPF application, I have a number of databound TextBoxes. The UpdateSourceTrigger for these bindings is LostFocus. The object is saved using the File menu. The problem I have is that it is possible to enter a new value into a TextBox, select Save from the File menu, and never persist the new value (the one visible in the TextBox) because accessing the menu does not remove focus from the TextBox. How can I fix this? Is there some way to force all the controls in a page to databind?
#palehorse: Good point. Unfortunately, I need to use LostFocus as my UpdateSourceTrigger in order to support the type of validation I want.
#dmo: I had thought of that. It seems, however, like a really inelegant solution for a relatively simple problem. Also, it requires that there be some control on the page which is is always visible to receive the focus. My application is tabbed, however, so no such control readily presents itself.
#Nidonocu: The fact that using the menu did not move focus from the TextBox confused me as well. That is, however, the behavior I am seeing. The following simple example demonstrates my problem:
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="MyItemProvider" />
</Window.Resources>
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Save" Click="MenuItem_Click" />
</MenuItem>
</Menu>
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA}" />
<TextBox Text="{Binding ValueB}" />
</StackPanel>
</DockPanel>
</Window>
using System;
using System.Text;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication2
{
public partial class Window1 : Window
{
public MyItem Item
{
get { return (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance as MyItem; }
set { (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance = value; }
}
public Window1()
{
InitializeComponent();
Item = new MyItem();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(string.Format("At the time of saving, the values in the TextBoxes are:\n'{0}'\nand\n'{1}'", Item.ValueA, Item.ValueB));
}
}
public class MyItem
{
public string ValueA { get; set; }
public string ValueB { get; set; }
}
}
I found that removing the menu items that are scope depended from the FocusScope of the menu causes the textbox to lose focus correctly. I wouldn't think this applies to ALL items in Menu, but certainly for a save or validate action.
<Menu FocusManager.IsFocusScope="False" >
Assuming that there is more than one control in the tab sequence, the following solution appears to be complete and general (just cut-and-paste)...
Control currentControl = System.Windows.Input.Keyboard.FocusedElement as Control;
if (currentControl != null)
{
// Force focus away from the current control to update its binding source.
currentControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
currentControl.Focus();
}
This is a UGLY hack but should also work
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox != null)
{
focusedTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
This code checks if a TextBox has focus... If 1 is found... update the binding source!
Suppose you have a TextBox in a window, and a ToolBar with a Save button in it. Assume the TextBox’s Text property is bound to a property on a business object, and the binding’s UpdateSourceTrigger property is set to the default value of LostFocus, meaning that the bound value is pushed back to the business object property when the TextBox loses input focus. Also, assume that the ToolBar’s Save button has its Command property set to ApplicationCommands.Save command.
In that situation, if you edit the TextBox and click the Save button with the mouse, there is a problem. When clicking on a Button in a ToolBar, the TextBox does not lose focus. Since the TextBox’s LostFocus event does not fire, the Text property binding does not update the source property of the business object.
Obviously you should not validate and save an object if the most recently edited value in the UI has not yet been pushed into the object. This is the exact problem Karl had worked around, by writing code in his window that manually looked for a TextBox with focus and updated the source of the data binding. His solution worked fine, but it got me thinking about a generic solution that would also be useful outside of this particular scenario. Enter CommandGroup…
Taken from Josh Smith’s CodeProject article about CommandGroup
Simple solution is update the Xaml code as shown below
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding ValueB, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
I've run into this issue and the best solution I've found was to change the focusable value of the button (or any other component such as MenuItem) to true:
<Button Focusable="True" Command="{Binding CustomSaveCommand}"/>
The reason it works, is because it forces the button to get focused before it invokes the command and therefore makes the TextBox or any other UIElement for that matter to loose their focus and raise lost focus event which invokes the binding to be changed.
In case you are using bounded command (as I was pointing to in my example), John Smith's great solution won't fit very well since you can't bind StaticExtension into bounded property (nor DP).
Have you tried setting the UpdateSourceTrigger to PropertyChanged? Alternatively, you could call the UpdateSOurce() method, but that seems like a bit overkill and defeats the purpose of TwoWay databinding.
Could you set the focus somewhere else just before saving?
You can do this by calling focus() on a UI element.
You could focus on whatever element invokes the "save". If your trigger is LostFocus then you have to move the focus somewhere. Save has the advantage that it isn't modified and would make sense to the user.
Since I noticed this issue is still a pain in the ass to solve on a very generic way, I tried various solutions.
Eventually one that worked out for me:
Whenever the need is there that UI changes must be validated and updated to its sources (Check for changes upon closeing a window, performing Save operations, ...), I call a validation function which does various things:
- make sure a focused element (like textbox, combobox, ...) loses its focus which will trigger default updatesource behavior
- validate any controls within the tree of the DependencyObject which is given to the validation function
- set focus back to the original focused element
The function itself returns true if everything is in order (validation is succesful) -> your original action (closeing with optional asking confirmation, saveing, ...) can continue. Otherwise the function will return false and your action cannot continue because there are validation errors on one or more elements (with the help of a generic ErrorTemplate on the elements).
The code (validation functionality is based on the article Detecting WPF Validation Errors):
public static class Validator
{
private static Dictionary<String, List<DependencyProperty>> gdicCachedDependencyProperties = new Dictionary<String, List<DependencyProperty>>();
public static Boolean IsValid(DependencyObject Parent)
{
// Move focus and reset it to update bindings which or otherwise not processed until losefocus
IInputElement lfocusedElement = Keyboard.FocusedElement;
if (lfocusedElement != null && lfocusedElement is UIElement)
{
// Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions)
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Keyboard.ClearFocus();
}
if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible)
return true;
// Validate all the bindings on the parent
Boolean lblnIsValid = true;
foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent))
{
if (BindingOperations.IsDataBound(Parent, aDependencyProperty))
{
// Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated
BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty);
if (lbindingExpressionBase != null)
{
lbindingExpressionBase.ValidateWithoutUpdate();
if (lbindingExpressionBase.HasError)
lblnIsValid = false;
}
}
}
if (Parent is Visual || Parent is Visual3D)
{
// Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs)
Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent);
for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++)
if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex)))
lblnIsValid = false;
}
if (lfocusedElement != null)
lfocusedElement.Focus();
return lblnIsValid;
}
public static List<DependencyProperty> GetAllDependencyProperties(DependencyObject DependencyObject)
{
Type ltype = DependencyObject.GetType();
if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName))
return gdicCachedDependencyProperties[ltype.FullName];
List<DependencyProperty> llstDependencyProperties = new List<DependencyProperty>();
List<FieldInfo> llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList();
foreach (FieldInfo aFieldInfo in llstFieldInfos)
llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty);
gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties);
return llstDependencyProperties;
}
}
The easiest way is to set the focus somewhere.
You can set the focus back immediately, but setting the focus anywhere will trigger the LostFocus-Event on any type of control and make it update its stuff:
IInputElement x = System.Windows.Input.Keyboard.FocusedElement;
DummyField.Focus();
x.Focus();
Another way would be to get the focused element, get the binding element from the focused element, and trigger the update manually. An example for TextBox and ComboBox (you would need to add any control type you need to support):
TextBox t = Keyboard.FocusedElement as TextBox;
if ((t != null) && (t.GetBindingExpression(TextBox.TextProperty) != null))
t.GetBindingExpression(TextBox.TextProperty).UpdateSource();
ComboBox c = Keyboard.FocusedElement as ComboBox;
if ((c != null) && (c.GetBindingExpression(ComboBox.TextProperty) != null))
c.GetBindingExpression(ComboBox.TextProperty).UpdateSource();
What do you think about this? I believe I've figured out a way to make it a bit more generic using reflection. I really didn't like the idea of maintaining a list like some of the other examples.
var currentControl = System.Windows.Input.Keyboard.FocusedElement;
if (currentControl != null)
{
Type type = currentControl.GetType();
if (type.GetMethod("MoveFocus") != null && type.GetMethod("Focus") != null)
{
try
{
type.GetMethod("MoveFocus").Invoke(currentControl, new object[] { new TraversalRequest(FocusNavigationDirection.Next) });
type.GetMethod("Focus").Invoke(currentControl, null);
}
catch (Exception ex)
{
throw new Exception("Unable to handle unknown type: " + type.Name, ex);
}
}
}
See any problems with that?
Using BindingGroup will help to understand and mitigate this kind of problem.
Sometimes we consider to apply MVVM model against WPF data bindings.
For example, we consider about mail's subject property:
<TextBox x:Name="SubjectTextBox" Text="{Binding Subject}" />
TextBox SubjectTextBox is on side of View.
The bound property like ViewModel.Subject will belong to ViewModel.
The problem is that changes remain to View in this case.
When we close the WPF window, WPF TextBox won't loose focus on window close.
It means data binding won't perform writing back, and then changes are lost silently.
Introducing of BindingGroup helps to control whether we should apply changes: from View to ViewModel.
BindingGroup.CommitEdit(); will ensure apply changes of direction View → ViewModel
BindingGroup.CancelEdit(); will ensure to discard changes on View.
If you don't call neither, changes are lost silently!
In the following sample, we attach RibbonWindow_Closing event handler so that we can deal with this case of problem.
XAML:
<R:RibbonWindow Closing="RibbonWindow_Closing" ...>
<FrameworkElement.BindingGroup>
<BindingGroup />
</FrameworkElement.BindingGroup>
...
</R:RibbonWindow>
C#
private void RibbonWindow_Closing(object sender, CancelEventArgs e) {
e.Cancel = !NeedSave();
}
bool NeedSave() {
if (!BindingGroup.CommitEdit()) {
// There may be validation error.
return false; // changes this to true to allow closing.
}
// Insert your business code to check modifications.
// return true; if Saved/DontSave/NotChanged
// return false; if Cancel
}
It should work.