简体   繁体   中英

WPF MVVM Managing multiple windows

Looking to write a note application in WPF/C# MVVM, not because we need another one but because it'll help me go over a few things I'm trying to become more familiar with. My question is regarding some guidance on how to handle multiple windows. For example there will be multiple notes not tied to a main window. I'd like to keep track of all open windows, for example if one received focus, I may want to bring the other note windows to the front, but not remove focus from the one I selected, not looking for anyone to give me the code, just some guidance on how to handle it.

Maybe this can help you in some way: http://kentb.blogspot.nl/2011/11/application-im-currently-working-on-top.html

The important parts from the article:

What I did instead was had the WindowItemsControl create WindowItemsControlItem instances as containers. These containers are really just surrogates for the Window they represent. When they're initialized, they display the Window. When they're destroyed, they close the Window. In addition, if a Window is closed ahead of time, the corresponding data item is removed from the underlying collection and thus too the surrogate from the visual tree.

public class WindowItemsControl : ItemsControl
{
    public static readonly DependencyProperty ShowDialogProperty = DependencyProperty.Register(
        "ShowDialog",
        typeof(bool),
        typeof(WindowItemsControl));
 
    public static readonly DependencyProperty OwnerProperty = DependencyProperty.Register(
        "Owner",
        typeof(Window),
        typeof(WindowItemsControl),
        new FrameworkPropertyMetadata(OnOwnerChanged));
 
    public static readonly DependencyProperty WindowStartupLocationProperty = DependencyProperty.Register(
        "WindowStartupLocation",
        typeof(WindowStartupLocation),
        typeof(WindowItemsControl));
 
    public static readonly DependencyProperty RemoveDataItemWhenWindowClosedProperty = DependencyProperty.Register(
        "RemoveDataItemWhenWindowClosed",
        typeof(bool),
        typeof(WindowItemsControl),
        new FrameworkPropertyMetadata(true));
 
    static WindowItemsControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowItemsControl), new FrameworkPropertyMetadata(typeof(WindowItemsControl)));
    }
 
    public bool ShowDialog
    {
        get { return (bool)this.GetValue(ShowDialogProperty); }
        set { this.SetValue(ShowDialogProperty, value); }
    }
 
    public Window Owner
    {
        get { return this.GetValue(OwnerProperty) as Window; }
        set { this.SetValue(OwnerProperty, value); }
    }
 
    public WindowStartupLocation WindowStartupLocation
    {
        get { return (WindowStartupLocation)this.GetValue(WindowStartupLocationProperty); }
        set { this.SetValue(WindowStartupLocationProperty, value); }
    }
 
    public bool RemoveDataItemWhenWindowClosed
    {
        get { return (bool)this.GetValue(RemoveDataItemWhenWindowClosedProperty); }
        set { this.SetValue(RemoveDataItemWhenWindowClosedProperty, value); }
    }
 
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new WindowItemsControlItem(this);
    }
 
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is WindowItemsControlItem;
    }
 
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        (element as WindowItemsControlItem).Window.Content = item;
    }
 
    protected override bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
    {
        // the item container style will be applied to the windows, not to the containers (which are surrogates for the window)
        return false;
    }
 
    private static void OnOwnerChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var windowItemsControl = (WindowItemsControl)dependencyObject;
        var owner = (Window)e.NewValue;
 
        for (var i = 0; i < windowItemsControl.Items.Count; ++i)
        {
            var container = windowItemsControl.ItemContainerGenerator.ContainerFromIndex(i) as WindowItemsControlItem;
 
            if (container == null)
            {
                continue;
            }
 
            container.Window.Owner = owner;
        }
    }
}  
  • it declares some properties (ShowDialog, Owner, WindowStartupLocation) that assist it in the display of child Windows
  • it declares a RemoveDataItemWhenWindowClosed property that can be used to prevent the control from removing data items when a window is closed. This can be useful in shutdown or other situations where windows are being closed programmatically rather than by the user
  • I don't apply the ItemContainerStyle to the containers themselves, but instead hold out so that I can apply them to the Windows they represent
  • I also make sure that any change of Owner is applied to any existing Windows
  • the default style is overridden to remove unnecessary stuff like the Border, because the WindowItemsControl will never actually be visible on screen
public class WindowItemsControlItem : FrameworkElement
{
    private readonly WindowItemsControl windowItemsControl;
    private readonly Window window;
 
    static WindowItemsControlItem()
    {
        // there is no need for these items to be visible as they are simply surrogates for the windows that they display
        VisibilityProperty.OverrideMetadata(typeof(WindowItemsControlItem), new FrameworkPropertyMetadata(Visibility.Collapsed));
    }
 
    public WindowItemsControlItem(WindowItemsControl windowItemsControl)
    {
        windowItemsControl.AssertNotNull("windowItemsControl");
 
        this.windowItemsControl = windowItemsControl;
        this.window = this.CreateWindow(windowItemsControl);
 
        this.Loaded += delegate
        {
            if (this.windowItemsControl.ShowDialog)
            {
                this.window.ShowDialog();
            }
            else
            {
                this.window.Show();
            }
        };
 
        this.Unloaded += delegate
        {
            this.window.Close();
        };
    }
 
    public Window Window
    {
        get { return this.window; }
    }
 
    private Window CreateWindow(WindowItemsControl windowItemsControl)
    {
        var window = new Window
        {
            Owner = windowItemsControl.Owner,
            WindowStartupLocation = windowItemsControl.WindowStartupLocation
        };
 
        BindingOperations.SetBinding(window, Window.DataContextProperty, new Binding("Content") { Source = window });
        BindingOperations.SetBinding(window, Window.StyleProperty, new Binding("ItemContainerStyle") { Source = windowItemsControl });
        BindingOperations.SetBinding(window, Window.ContentTemplateProperty, new Binding("ItemTemplate") { Source = windowItemsControl });
        BindingOperations.SetBinding(window, Window.ContentTemplateSelectorProperty, new Binding("ItemTemplateSelector") { Source = windowItemsControl });
 
        window.Closed += delegate
        {
            // orphan the content because it might be hosted somewhere else later (including in another window)
            window.Content = null;
 
            // if the window closes, attempt to remove the original item from the underlying collection, which will result in this surrogate being removed too
            if (windowItemsControl.RemoveDataItemWhenWindowClosed)
            {
                var editableItems = windowItemsControl.Items as IEditableCollectionView;
 
                if (editableItems != null && editableItems.CanRemove)
                {
                    editableItems.Remove(this.DataContext);
                }
            }
        };
 
        return window;
    }
}
  • relevant properties on the WindowItemsControl are bound to the correct properties on the Windows themselves
  • Windows are displayed when the surrogate is initialized, and closed when the surrogate is unloaded
  • as mentioned earlier, Windows that are closed before the surrogate is destroyed (perhaps by the user clicking the close button) result in the related data item in the underlying collection being removed (unless the RemoveDataItemWhenWindowClosed property has been set to false). This, in turn, will cause the surrogate to be removed from the visual tree. In other words, if I close a widget Window, the corresponding WidgetViewModel will be removed from my collection of widget view models. Then, the ItemsControl will remove the related surrogate container from the visual tree.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM