I am testing the possibility to modify dynamically the structure of Grid control (number of Rows/Columns for example).
I am using last version Caliburn.Micro and Ninject and use a GridHelpers (i have modified) to have the possibility to bind number of Rows and Columns.
The full code of my application is available on Github
I have created an usercontrol 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>
the view model associated:
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)
{
}
}
}
the Main2View has a content control and all the settings to define the structure of the grid defined in the usercontrol.
<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>
the code of Main2ViewModel:
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);
}
}
}
All is fine during the intialization of view/viewmodel, but the problem begins when i change the value of RowCount or RowColumn and i click on button.
i am waiting the initializatoon of new view , but its not the case, the view MyGridView is not reloaded..
this is the boostrapper:
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);
}
}
}
I dont arrive to relaunch a new view even i use InTransientScope() which indicates to use a new instance of view.. but maybe i am missing something..
In anyways it seems the applicationto have a problem, because when i close the main view, the debugger doesnt finish and i have to stop the application with the red button in VS2022
i have tested with the use of Unbind but no luck
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);
}
Ok i have found the solution:
In Main2ViewModel
, just refactor my variable myGridViewModel with NotifyOfPropertyChange()
:
private MyGridViewModel _myGridViewModel;
public MyGridViewModel myGridViewModel
{
get { return _myGridViewModel; }
set
{
_myGridViewModel = value;
NotifyOfPropertyChange(() => myGridViewModel);
}
}
so i can write the method AddNewContent like this:
private void AddNewContent()
{
ButtonViewModels = new BindableCollection<ButtonViewModel>(CreateButton());
myGridViewModel = new MyGridViewModel(this);
}
so in bootstrapper.cs
file, no need to Bind MyGridViewModel :
protected override void Configure()
{
kernel = new StandardKernel();
kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
}
And no more problem when i quit application