Resolving windows in Structure Map or how to manage multiple windows in WPF MVVM?

前端 未结 3 819
伪装坚强ぢ
伪装坚强ぢ 2020-12-14 21:09

I have been reading Mark Seeman\'s book on dependency injection in .NET and I\'m struggling to configure composition root in WPF application.

My container will be re

3条回答
  •  旧时难觅i
    2020-12-14 21:39

    I think before implement patterns of behavior, such as a Mediator, and the like, need to decide on a generic pattern for easy application structure. For this purpose, namely, for the create independent windows, well suited Abstract factory pattern.

    Creation of the windows can be implemented on the side ViewModel using methods such as IDialogService. But I think that this task should be implemented on the side View, because the Window object refers to the View and not to ViewModel. So, you must create MVVM style architecture that it allows create independent windows using design patterns.

    I created a project in which an Abstract factory creates a Window on the side of the View using the attached behavior. Abstract factory also implements the Singleton pattern to create a global point of access and to ensure the uniqueness of the newly constructed object. Attached behavior implicitly implements pattern Decorator who is a wrapper for an abstract factory that is used on the side of XAML. To an Abstract factory does not refer to objects which are located in ViewModel is used a Proxy pattern which is a ContentControl with DataTemplate without DataType. Also used Command pattern for independent action between objects. As a result, this project uses the following patterns:

    • Abstract factory
    • Singleton
    • Decorator
    • Proxy
    • Command

    The project structure looks like this:

    enter image description here

    In the attached behavior has attached dependency property Name, which is transmitted in the name of the new window. For him registered PropertyChangedEvent, which is a call Make method an abstract factory:

    private static void IsFactoryStart(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var window = sender as Window;
    
        if (window == null)
        {
            return;
        }
    
        if (e.NewValue is String && String.IsNullOrEmpty((string)e.NewValue) == false)
        {
            _typeWindow = (string)e.NewValue;
    
            if (_typeWindow != null)
            {
                var newWindow = WindowFactory.Instance.Make(_typeWindow);
                newWindow.Show();
            }        
        }
    }
    

    WindowFactory together with the Singleton pattern looks like this:

    public class WindowFactory : IWindowFactory
    {
        #region WindowFactory Singleton Instance
    
        private static WindowFactory _instance = null;
        private static readonly object padlock = new object();
    
        public static WindowFactory Instance
        {
            get
            {
                lock (padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new WindowFactory();
                    }
    
                    return _instance;
                }
            }
        }
    
        #endregion
    
        public Window Make(string TypeWindow)
        {
            if (TypeWindow.Equals("WindowOneViewProxy"))
            {
                var windowOne = new Window();                
    
                windowOne.Width = 450;
                windowOne.Height = 250;
                windowOne.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                windowOne.Title = TypeWindow;
                windowOne.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
    
                return windowOne;
            }
            else if (TypeWindow.Equals("WindowTwoViewProxy"))
            {
                var windowTwo = new Window();
                windowTwo.Width = 500;
                windowTwo.Height = 200;
                windowTwo.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                windowTwo.Title = TypeWindow;
                windowTwo.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
    
                return windowTwo;
            }
            else if (TypeWindow.Equals("WindowThreeViewProxy")) 
            {
                var windowThree = new Window();
                windowThree.Width = 400;
                windowThree.Height = 140;
                windowThree.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                windowThree.Title = TypeWindow;
                windowThree.ContentTemplate = Application.Current.Resources[TypeWindow] as DataTemplate;
    
                return windowThree;
            }
            else
                throw new Exception("Factory can not create a: {0}" + TypeWindow);
        }
    }
    

    For the property Window.ContentTemplate set DataTemplate from resources. ContentTemplate is responsible for the visual representation, in order to bind properties from ViewModel, you need to set the object to Content. But in this case, the Abstract factory reference will to ViewModel, and to avoid them and using the proxy pattern as follows:

    WindowOneProxyView

    
        
            
        
    
    

    WindowOneViewRealObject

    
        
            

    In DataTemplate proxy is not specified DataType, but it is in the real object.

    In MainViewModel has commands to simply set the window name, which will give input for attached behavior:

    MainModel

    public class MainModel : NotificationObject
    {
        #region TypeName
    
        private string _typeName = null;
    
        public string TypeName
        {
            get
            {
                return _typeName;
            }
    
            set
            {
                _typeName = value;
                NotifyPropertyChanged("TypeName");
            }
        }
    
        #endregion
    }
    

    MainViewModel

    public class MainViewModel
    {
        #region MainModel
    
        private MainModel _mainModel = null;
    
        public MainModel MainModel
        {
            get
            {
                return _mainModel;
            }
    
            set
            {
                _mainModel = value;
            }
        }
    
        #endregion
    
        #region ShowWindowOneCommand
    
        private ICommand _showWindowOneCommand = null;
    
        public ICommand ShowWindowOneCommand
        {
            get
            {
                if (_showWindowOneCommand == null)
                {
                    _showWindowOneCommand = new RelayCommand(param => this.ShowWindowOne(), null);
                }
    
                return _showWindowOneCommand;
            }
        }
    
        private void ShowWindowOne()
        {
            MainModel.TypeName = "WindowOneViewProxy";
        }
    
        #endregion
    
        #region ShowWindowTwoCommand
    
        private ICommand _showWindowTwoCommand = null;
    
        public ICommand ShowWindowTwoCommand
        {
            get
            {
                if (_showWindowTwoCommand == null)
                {
                    _showWindowTwoCommand = new RelayCommand(param => this.ShowWindowTwo(), null);
                }
    
                return _showWindowTwoCommand;
            }
        }
    
        private void ShowWindowTwo()
        {
            MainModel.TypeName = "WindowTwoViewProxy";
        }
    
        #endregion
    
        #region ShowWindowThreeCommand
    
        private ICommand _showWindowThreeCommand = null;
    
        public ICommand ShowWindowThreeCommand
        {
            get
            {
                if (_showWindowThreeCommand == null)
                {
                    _showWindowThreeCommand = new RelayCommand(param => this.ShowWindowThree(), null);
                }
    
                return _showWindowThreeCommand;
            }
        }
    
        private void ShowWindowThree()
        {
            MainModel.TypeName = "WindowThreeViewProxy";
        }
    
        #endregion
    
        public MainViewModel() 
        {
            MainModel = new MainModel();
        }
    }
    

    MainWindow looks as:

     
    
    
        
    
    
    
        

    Test View-ViewModel for the first window looks like this (they practically identical):

    WindowOneModel

    public class WindowOneModel : NotificationObject
    {
        #region TextContent
    
        private string _textContent = "Text content for WindowOneView";
    
        public string TextContent
        {
            get
            {
                return _textContent;
            }
    
            set
            {
                _textContent = value;
                NotifyPropertyChanged("TextContent");
            }
        }
    
        #endregion
    }
    

    WindowOneViewModel

    public class WindowOneViewModel
    {
        #region WindowOneModel
    
        private WindowOneModel _windowOneModel = null;
    
        public WindowOneModel WindowOneModel
        {
            get
            {
                return _windowOneModel;
            }
    
            set
            {
                _windowOneModel = value;
            }
        }
    
        #endregion
    
        #region OneCommand
    
        private ICommand _oneCommand = null;
    
        public ICommand OneCommand
        {
            get
            {
                if (_oneCommand == null)
                {
                    _oneCommand = new RelayCommand(param => this.One(), null);
                }
    
                return _oneCommand;
            }
        }
    
        private void One()
        {
             WindowOneModel.TextContent = "Command One change TextContent";
        }
    
        #endregion
    
        public WindowOneViewModel() 
        {
            WindowOneModel = new WindowOneModel();
        }
    }
    

    This project is available at this link.

    Output

    MainWindow

    enter image description here

    WindowOne

    enter image description here

    WindowTwo

    enter image description here

    WindowThree

    enter image description here

提交回复
热议问题