wpf 如何在MVVM中使用Caliburn.Micro和Ninject重用新视图

svgewumm  于 2023-04-22  发布在  其他
关注(0)|答案(1)|浏览(133)

我正在测试的可能性,以动态修改结构的网格控制(行数/列数为例)。
我使用的是最新版本的Caliburn.Micro和Ninject,并使用GridHelpers(我已经修改)来绑定行数和列数。
我的应用程序的完整代码可以在Github上找到。
我创建了一个用户控件MyGridView:

<UserControl x:Class="GridHelpersSample.MyGridView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:gh="clr-namespace:GridHelpers;assembly=GridHelpers"
             xmlns:cal="http://www.caliburnproject.org"
             xmlns:local="clr-namespace:GridHelpersSample"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ItemsControl ItemsSource="{Binding ButtonViewModels}">
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding GridRow}" />
                    <Setter Property="Grid.Column" Value="{Binding GridColumn}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate >
                <DataTemplate >
                    <Button cal:Message.Attach="[Event Click] = [Action OnButtonClick($dataContext)]"
                                Content="{Binding Content}"/>
                </DataTemplate >
            </ItemsControl.ItemTemplate >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid ShowGridLines="true" 
                        gh:GridHelpers.RowCount = "{Binding RowCount, Mode=TwoWay}"
                              gh:GridHelpers.ColumnCount = "{Binding ColumnCount, Mode=TwoWay}"
                              gh:GridHelpers.StarRows = "{Binding StarRows, Mode=TwoWay}"
                              gh:GridHelpers.StarColumns = "{Binding StarColumns, Mode=TwoWay}"
                              gh:GridHelpers.PixelRows = "{Binding PixelRows, Mode=TwoWay}"
                              gh:GridHelpers.PixelColumns = "{Binding PixelColumns, Mode=TwoWay}" >

                    </Grid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <!-- Now define a container that will host the ItemsControl -->
            <!--<ItemsControl.ItemsPanel >
                    <ItemsPanelTemplate >
                        <VirtualizingStackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate >
                </ItemsControl.ItemsPanel >-->
        </ItemsControl >
    </Grid>
</UserControl>

关联的视图模型:

using Caliburn.Micro;

namespace GridHelpersSample
{
    public class MyGridViewModel : Screen
    {
        public BindableCollection<ButtonViewModel> ButtonViewModels { get; set; }

        public MyGridViewModel(Main2ViewModel main2ViewModel)
        {
            RowCount = main2ViewModel.RowCount;
            ColumnCount = main2ViewModel.ColumnCount;
            StarRows = main2ViewModel.StarRows;
            StarColumns = main2ViewModel.StarColumns;
            PixelRows = main2ViewModel.PixelRows;
            PixelColumns = main2ViewModel.PixelColumns;
            ButtonViewModels = main2ViewModel.ButtonViewModels;
        }

        #region datas for defining grid
        private string rowCount;
        public string RowCount
        {
            get { return rowCount; }
            set
            {
                rowCount = value;
                NotifyOfPropertyChange(() => RowCount);
            }
        }
        private string columnCount;
        public string ColumnCount
        {
            get { return columnCount; }
            set
            {
                columnCount = value;
                NotifyOfPropertyChange(() => ColumnCount);
            }
        }
        private string starRows;
        public string StarRows
        {
            get { return starRows; }
            set
            {
                starRows = value;
                NotifyOfPropertyChange(() => StarRows);
            }
        }
        private string starColumns;
        public string StarColumns
        {
            get { return starColumns; }
            set
            {
                starColumns = value;
                NotifyOfPropertyChange(() => StarColumns);
            }
        }

        private string pixelRows;
        public string PixelRows
        {
            get { return pixelRows; }
            set
            {
                pixelRows = value;
                NotifyOfPropertyChange(() => PixelRows);
            }
        }
        private string pixelColumns;

        public string PixelColumns
        {
            get { return pixelColumns; }
            set
            {
                pixelColumns = value;
                NotifyOfPropertyChange(() => PixelColumns);
            }
        }
        #endregion

        public void OnButtonClick(ButtonViewModel context)
        {

        }
    }
}

Main 2 View有一个内容控件和所有的设置来定义在用户控件中定义的网格的结构。

<Window x:Class="GridHelpersSample.Main2View"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:gh="clr-namespace:GridHelpers;assembly=GridHelpers"
        xmlns:cal="http://www.caliburnproject.org"       
        xmlns:local="clr-namespace:GridHelpersSample"
        mc:Ignorable="d"
        Title="Main2View" Height="600" Width="1000">
    <Window.Resources>
        <Style x:Key="LabelStyle" TargetType="Label">
            <Setter Property="Width" Value="120" />
            <Setter Property="Margin" Value="5" />
            <Setter Property="FontSize" Value="16" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Foreground" Value="Blue" />
        </Style>
        <Style x:Key="TextBoxStyle" TargetType="TextBox">
            <Setter Property="Width" Value="120" />
            <Setter Property="Margin" Value="5" />
            <Setter Property="FontSize" Value="16" />
            <Setter Property="FontWeight" Value="Regular" />
            <Setter Property="Foreground" Value="Black" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <!--<Setter Property="cal:Message.Attach" Value="[Event LostFocus] = [Action TextBox_LostFocus($source, $this)]" />-->
            <Style.Triggers>
                <Trigger Property="Name" Value="RowCount">
                    <Setter Property="Width" Value="30"/>
                </Trigger>
                <Trigger Property="Name" Value="ColumnCount">
                    <Setter Property="Width" Value="30"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid x:Name="MainGrid"
        gh:GridHelpers.RowCount="2"
        gh:GridHelpers.ColumnCount="1"
        gh:GridHelpers.StarRows="1"
        gh:GridHelpers.StarColumns="*">
        <StackPanel Orientation="Vertical" Grid.Row="0" >
            <Separator />
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Label Content="RowCount:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="RowCount" Style="{StaticResource TextBoxStyle}" />
                <Label Content="StarRows:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="StarRows" Style="{StaticResource TextBoxStyle}" />
                <Label Content="PixelRows:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="PixelRows" Style="{StaticResource TextBoxStyle}" />
                <Button x:Name="Valider" Content="Valider la saisie" Width="100" 
                        Margin="30,20,0,-20" />
            </StackPanel>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Label Content="ColumnCount:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="ColumnCount" Style="{StaticResource TextBoxStyle}" />
                <Label Content="StarColumns:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="StarColumns" Style="{StaticResource TextBoxStyle}" />
                <Label Content="PixelColumns:" Style="{StaticResource LabelStyle}" />
                <TextBox x:Name="PixelColumns" Style="{StaticResource TextBoxStyle}" />
                <Button Width="100" Visibility="Hidden" Height="0" Margin="30,0,0,0"/>
            </StackPanel>
            <Separator />
        </StackPanel>
        <ContentControl x:Name="myGridViewModel" Grid.Row="1"/>
    </Grid>
</Window>

Main 2 ViewModel的代码:

using Caliburn.Micro;
using Ninject;
using Ninject.Syntax;
using System.Collections.Generic;

namespace GridHelpersSample
{
    public class Main2ViewModel : Screen
    {
        private readonly IResolutionRoot _resolutionRoot;
        private readonly IKernel _kernel;
        public BindableCollection<ButtonViewModel> ButtonViewModels { get; set; }
        public MyGridViewModel myGridViewModel { get; set; }
        public Main2ViewModel(IResolutionRoot resolutionRoot, IKernel kernel) 
        {
            _resolutionRoot = resolutionRoot;
            _kernel = kernel;
            RowCount = "3";
            ColumnCount = "3";
            StarRows = "*";
            StarColumns = "*";
            PixelRows = "";
            PixelColumns = "";

            AddNewContent();
        }

        #region datas for defining grid
        private string rowCount;
        public string RowCount
        {
            get { return rowCount; }
            set
            {
                rowCount = value;
                NotifyOfPropertyChange(() => RowCount);
            }
        }
        private string columnCount;
        public string ColumnCount
        {
            get { return columnCount; }
            set
            {
                columnCount = value;
                NotifyOfPropertyChange(() => ColumnCount);
            }
        }
        private string starRows;
        public string StarRows
        {
            get { return starRows; }
            set
            {
                starRows = value;
                NotifyOfPropertyChange(() => StarRows);
            }
        }
        private string starColumns;
        public string StarColumns
        {
            get { return starColumns; }
            set
            {
                starColumns = value;
                NotifyOfPropertyChange(() => StarColumns);
            }
        }

        private string pixelRows;
        public string PixelRows
        {
            get { return pixelRows; }
            set
            {
                pixelRows = value;
                NotifyOfPropertyChange(() => PixelRows);
            }
        }
        private string pixelColumns;

        public string PixelColumns
        {
            get { return pixelColumns; }
            set
            {
                pixelColumns = value;
                NotifyOfPropertyChange(() => PixelColumns);
            }
        }
        #endregion

        public void Valider()
        {            
            AddNewContent();
        }
        public List<ButtonViewModel> CreateButton()
        {
            var myView = _resolutionRoot.Get<Main2View>();
            var t = myView.MainGrid;
            var list = new List<ButtonViewModel>();
            for (int i = 0; i < int.Parse(RowCount); i++)
            {
                for (int j = 0; j < int.Parse(ColumnCount); j++)
                {
                    var button = new ButtonViewModel
                    {
                        Content = $"R{i} C{j}",
                        GridRow = i,
                        GridColumn = j
                    };

                    list.Add(button);
                }
            }
            return list;
        }

        private void AddNewContent()
        {
            ButtonViewModels = new BindableCollection<ButtonViewModel>(CreateButton());
            myGridViewModel = new MyGridViewModel(this);
        }
    }
}

在初始化视图/视图模型时一切都很好,但是当我更改RowCount或RowColumn的值并单击按钮时,问题就开始了。
我正在等待新视图的初始化,但情况并非如此,视图MyGridView没有重新加载。
这就是boostapper:

using Caliburn.Micro;
using Ninject;
using System;
using System.Collections.Generic;
using System.Windows;

namespace GridHelpersSample
{
    public class Bootstrapper : BootstrapperBase
    {
        private IKernel kernel;
        public Bootstrapper()
        {
            Initialize();
        }

        protected override void Configure()
        {
            kernel = new StandardKernel();
            kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
            kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
            kernel.Bind<MyGridViewModel>().ToSelf().InTransientScope();
            //kernel.Bind<MyGridViewModel>().ToSelf().InSingletonScope();
            var bindings0 = kernel.GetBindings(typeof(MyGridViewModel));
            //var bindings1 = kernel.GetBindings(typeof(MainViewModel));
        }
        protected override async void OnStartup(object sender, StartupEventArgs e)
        {
            await DisplayRootViewForAsync<Main2ViewModel>();
        }
        protected override object GetInstance(Type service, string key)
        {
            return kernel.Get(service);
        }
        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return kernel.GetAll(service);
        }
        protected override void BuildUp(object instance)
        {
            kernel.Inject(instance);
        }
    }
}

我没有到达重新启动一个新的视图,即使我使用InTransientScope(),这表明使用一个新的视图示例..但也许我错过了一些东西..
无论如何,应用程序似乎有问题,因为当我关闭主视图时,调试器没有完成,我必须在VS 2022中使用红色按钮停止应用程序
我已经使用Unbind进行了测试,但没有成功

private void AddNewContent()
    {
        ButtonViewModels = new BindableCollection<ButtonViewModel>(CreateButton());
        if (myGridViewModel == null)
        {
            myGridViewModel = new MyGridViewModel(this);
            return;
        }
        //_kernel.Unbind<MyGridViewModel>();
        _kernel.Unbind<MyGridView>();
        //_kernel.Bind<MyGridViewModel>().To<MyGridViewModel>();
        _kernel.Bind<MyGridView>().To<MyGridView>();
        myGridViewModel = new MyGridViewModel(this);
    }
sulc1iza

sulc1iza1#

我找到了解决方案:
Main2ViewModel中,只需使用NotifyOfPropertyChange()重构我的变量myGridViewModel:

private MyGridViewModel _myGridViewModel;
    public MyGridViewModel myGridViewModel
    {
        get { return _myGridViewModel; }
        set
        {
            _myGridViewModel = value;
            NotifyOfPropertyChange(() => myGridViewModel);
        }
    }

所以我可以这样写AddNewContent方法:

private void AddNewContent()
    {
        ButtonViewModels = new BindableCollection<ButtonViewModel>(CreateButton());
        myGridViewModel = new MyGridViewModel(this);
    }

所以在bootstrapper.cs文件中,不需要绑定MyGridViewModel:

protected override void Configure()
    {
        kernel = new StandardKernel();
        kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
        kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
    }

当我退出应用程序时,没有更多的问题

相关问题