MDI Application and Child Windows

Aug 1, 2011 at 5:12 PM

I am trying to build the framework for a MDI style application in Silverlight.  One of the challenges that I am trying to work through is how to handle the window management and still stay true to the MVVM design.  I'm using the C1Window control for all my child windows, and setting the Tag property of the Window and a WindowTag property on the View and ViewModel to be able to keep track of what window is what and be able to close the windows at the correct time.  I also need to be able to set the c1window.canvas to a canvas on the main form so that things display properly.

I feel like I'm way over complicating this but I don't see a way that I can get the view to the c1window and then get the WindowTag back to the ViewModel when its time to close the window.  I'm currently using a generic Create/Close window message and had thought about using a unique message for each view but that doesn't seem right either. 

The way it works currently is:

MainViewModel

1.  Create the view.  2.  Set the windowTag property on the view.  3. Send the create window message with the view as a property of the message. 

MainView

4.  Create the C1Window and set its Content property to the View passed in via generic object type.  5.  Stores window in collection.  6.  Show Window.

MyQueuesView(New Window View)

7.  Set hidden text box (txtWindowText) to WindowTag property of View(This also sets the WindowTag on the MyQueuesViewModel, because its mode is TwoWay).

The window is now created.  To close the window now its starts with an event on the MyQueuesViewModel:

MyQueuesViewModel

1.  Send the CloseWindowMessage with Tag of window to close.

MainView

2.  Cycles through window collection and closes window with that tag property.

Any thoughts on ways that I could make this better?  I tried using inheritance and changing all my views to inherited from a ViewBase but I couldn't get that to work.  Silverlight seems to want only a "UserControl" as the root object in xaml.

Thanks

dbl

 

mainViewModel.cs:

    public class MainViewModel : ViewModelBase
    {

        public RelayCommand MyUserQueuesCommand { get; set; }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            RegisterCommands();
        }

        public void OnMyUserQueues()
        {
            var newContent = new MyQueuesView();
            newContent.WindowTag = System.Guid.NewGuid().ToString();

            Messenger.Default.Send(new CreateChildMessage()
            {
                Tag = newContent.WindowTag
                ,Header = "My Queues"
                ,Content = newContent
            });
        }

        protected void RegisterCommands()
        {
            MyUserQueuesCommand = new RelayCommand(OnMyUserQueues);
        }
    }

This event is then is listened to by the mainView:

mainView.xaml.cs

    public partial class MainView : UserControl
    {
        private List<C1Window> _windows = new List<C1Window>();

        /// <summary>
        /// Initializes a new instance of the MainC1View class.
        /// </summary>
        public MainView()
        {
            InitializeComponent();

            Messenger.Default.Register<CreateChildMessage>(this, CreateChildWindow);
            Messenger.Default.Register<CloseChildMessage>(this, CloseChildWindow);
        }

        public void CreateChildWindow(CreateChildMessage msg)
        {
            C1Window newWin;
            newWin = new C1Window();
            newWin.Header = msg.Header;
            newWin.Width = 600;
            newWin.Height = 420;
            newWin.MinHeight = 420;
            newWin.MinWidth = 600;
            newWin.Tag = msg.Tag;
            newWin.Content = msg.Content; //this is the view for the window.
            newWin.Canvas = mainCanvas; //this places the window inside the canvas so that things display properly.
            newWin.Show();
            _windows.Add(newWin);
        }

        public void CloseChildWindow(CloseChildMessage msg)
        {
            for (int i = _windows.Count - 1; i >= 0; i--)
            {
                C1Window childWin = (C1Window)_windows[i];

                if (childWin.Tag.ToString() == msg.Tag.ToString())
                {
                    _windows.Remove(childWin);
                    childWin.Close();
                    break;
                }

            }
        }
    }

The child window opened then has the following code:

myQueuesViewModel.cs

    public class MyQueuesViewModel : ViewModelBase
    {
        private string _windowTag = "";

        public RelayCommand CloseCommand { get; set; }

        /// <summary>
        /// Initializes a new instance of the MyQueuesC1ViewModel class.
        /// </summary>
        public MyQueuesViewModel()
        {
            RegisterCommands();
        }

        protected void RegisterCommands()
        {
            CloseCommand = new RelayCommand(OnClose);
        }

        private void OnClose()
        {
            Messenger.Default.Send(new CloseChildMessage() { Tag = WindowTag });
        }

        public string WindowTag
        {
            get
            {
                return _windowTag;
            }
            set
            {
                _windowTag = value;
            }
        }
    }

And its corresponding View is below.  I did not include the xaml just for brevity as the contents of the xaml page really don't make any difference.  The one piece on the xaml page that is pertinent is the hidden text field i'm using to store the window tag.  And have appended that to the bottom of this code section.

myQueuesView.xaml.cs

    public partial class MyQueuesView : UserControl
    {
        private string _windowTag = "";

        /// <summary>
        /// Initializes a new instance of the MyQueuesView class.
        /// </summary>
        public MyQueuesView(){
            InitializeComponent();
        }

        public string WindowTag
        {
            get
            {
                return _windowTag;
            }
            set
            {
                _windowTag = value;
            }
        }

        private void MyQueuesView_Loaded(object sender, RoutedEventArgs e)
        {
            this.txtWindowTag.Text = WindowTag;
        }
    }

myQueuesView.xaml (portion)

        <TextBox Name="txtWindowTag"
                 Text="{Binding Path=WindowTag, Mode=TwoWay}"
                 Visibility="Collapsed"
                 VerticalAlignment="Bottom"
                 HorizontalAlignment="Left"
                 Width="100" />