0

I have a ListBox with my menu items in it.

 <ListBox x:Name="ListBoxMenu"  SelectionChanged="ListBoxMenu_SelectionChanged"             
                 Grid.Column="0" Margin="0" Padding="0" Grid.Row="1" Width="{StaticResource LeftMenuWidth}"                 
                 ItemsSource="{Binding MenuItems}"
                 Background="{StaticResource ListBoxColor}"
                 BorderThickness="0"
                 SelectedIndex="0" VerticalAlignment="Stretch" >
 <ListBox.ItemTemplate>
                <DataTemplate>
                    <DockPanel>
                        <Image Source="{Binding MenuImage}" Height="20" Width="20" DockPanel.Dock="Left" Margin="5" />
                        <TextBlock Text="{Binding MenuName}" FontSize="{StaticResource MenuFontSize}" FontWeight="Bold" DockPanel.Dock="Left" Width="Auto" VerticalAlignment="Center" HorizontalAlignment="Left"/>
                    </DockPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

I kind of chopped up the code hope it still make since.

Then I have a control template that loads each user control.

<ContentControl Content="{Binding ElementName=ListBoxMenu, Path=SelectedItem}" Grid.Column="1" Grid.Row="1"/>

The Problem:

My problem is that I would like to test when a user leaves the user control if they have made any changes to ask them to save the changes. I already have NotifyPropertyChange working so that isn't the problem. I need to figure out how to see when the user is leaving the control / page.

What I have tried

As you can see I have added selectionchanged to the list box which technically does work however its not ideal because the usercontrol changes visually then the user is prompted to save any changes. I want to prompt them before they leave the user control.

SelectionChanged="ListBoxMenu_SelectionChanged"   
Ilan
  • 2,762
  • 1
  • 13
  • 24
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449

1 Answers1

0

Updates #1

There is more than one possible helpful suggestions to handle the "from view navigation", here are a couple of simple examples:

  1. In order to check if your control is active(I mean is shown to user), when there is no any navigation controllers in use, I think you can use the control's IsVisibleChanged event that indicates the control's IsVisible(true/false) state. In case you want to start the IsDirty logic when your control is partially visible you can use the @Evk guy suggestion(using of LostFocus), test the IsHitTestVisible on the control bounds and according to the test result(how more the control is hidden) you can start(or not) your desired logic.

Here is an example of IsHitTest visibilty(from this link)

    /// <summary>
    /// helps to indicate the partial IsVisible state
    /// </summary>
    /// <param name="element">elemnt under the test</param>
    /// <param name="container">parent window or control</param>
    /// <returns></returns>
    private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
    {
        if (!element.IsVisible)
            return false;

        Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
        Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
        return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
    }
  1. In case you have a navigation supporting control(something like a Frame) you can use its events to know that the navigation is started(aka you are going to move to another control), like FragmentNavigation.

In addition you should implement an IsDirty on your ViewModel. Here are a few examples of how to do that:

  1. MVVM - implementing 'IsDirty' functionality to a ModelView in order to save data
  2. Almost-automatic INotifyPropertyChanged, automatic IsDirty, and automatic ChangeTracking.

Here is some code sample for an IsDirty implementation(all credit to this guy)

/// <summary>
/// Provides a base class for objects that support property change notification 
/// and querying for changes and resetting of the changed status.
/// </summary>
public abstract class ViewModelBase : IChangeTracking, INotifyPropertyChanged
{
    //========================================================
    //  Constructors
    //========================================================
    #region ViewModelBase()
    /// <summary>
    /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
    /// </summary>
    protected ViewModelBase()
    {
        this.PropertyChanged += new PropertyChangedEventHandler(OnNotifiedOfPropertyChanged);
    }
    #endregion

    //========================================================
    //  Private Methods
    //========================================================
    #region OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e)
    /// <summary>
    /// Handles the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for this object.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that contains the event data.</param>
    private void OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal))
        {
            this.IsChanged = true;
        }
    }
    #endregion

    //========================================================
    //  IChangeTracking Implementation
    //========================================================
    #region IsChanged
    /// <summary>
    /// Gets the object's changed status.
    /// </summary>
    /// <value>
    /// <see langword="true"/> if the object’s content has changed since the last call to <see cref="AcceptChanges()"/>; otherwise, <see langword="false"/>. 
    /// The initial value is <see langword="false"/>.
    /// </value>
    public bool IsChanged
    {
        get
        {
            lock (_notifyingObjectIsChangedSyncRoot)
            {
                return _notifyingObjectIsChanged;
            }
        }

        protected set
        {
            lock (_notifyingObjectIsChangedSyncRoot)
            {
                if (!Boolean.Equals(_notifyingObjectIsChanged, value))
                {
                    _notifyingObjectIsChanged = value;

                    this.OnPropertyChanged("IsChanged");
                }
            }
        }
    }
    private bool _notifyingObjectIsChanged;
    private readonly object _notifyingObjectIsChangedSyncRoot = new Object();
    #endregion

    #region AcceptChanges()
    /// <summary>
    /// Resets the object’s state to unchanged by accepting the modifications.
    /// </summary>
    public void AcceptChanges()
    {
        this.IsChanged = false;
    }
    #endregion

    //========================================================
    //  INotifyPropertyChanged Implementation
    //========================================================
    #region PropertyChanged
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    #region OnPropertyChanged(PropertyChangedEventArgs e)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
    /// </summary>
    /// <param name="e">A <see cref="PropertyChangedEventArgs"/> that provides data for the event.</param>
    protected void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
    #endregion

    #region OnPropertyChanged(string propertyName)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for the specified <paramref name="propertyName"/>.
    /// </summary>
    /// <param name="propertyName">The <see cref="MemberInfo.Name"/> of the property whose value has changed.</param>
    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    #region OnPropertyChanged(params string[] propertyNames)
    /// <summary>
    /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event for the specified <paramref name="propertyNames"/>.
    /// </summary>
    /// <param name="propertyNames">An <see cref="Array"/> of <see cref="String"/> objects that contains the names of the properties whose values have changed.</param>
    /// <exception cref="ArgumentNullException">The <paramref name="propertyNames"/> is a <see langword="null"/> reference (Nothing in Visual Basic).</exception>
    protected void OnPropertyChanged(params string[] propertyNames)
    {
        if (propertyNames == null)
        {
            throw new ArgumentNullException("propertyNames");
        }

        foreach (var propertyName in propertyNames)
        {
            this.OnPropertyChanged(propertyName);
        }
    }
    #endregion
}

Let me know if you want more examples or code.

Community
  • 1
  • 1
Ilan
  • 2,762
  • 1
  • 13
  • 24