I have a TabViewModel wich contains a dependency property CurrentViewModel. The CurrentViewModel property is bound to a ContentControl in the view TabView.xaml. The TabViewModel also contains a command to change the CurrentViewModel to a ProductViewModel:
public class TabViewModel: BaseViewModel
{
public string TabName { get; set; }
//public List<BaseViewModel> ViewModels { get; set; }
private PageViewModel _currentViewModel;
public PageViewModel CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
OnPropertyChanged("CurrentViewModel");
}
}
public TabViewModel(string tabName, PageViewModel currentViewModel)
{
TabName = tabName;
CurrentViewModel = currentViewModel;
}
private ICommand _navigateToProductViewModelCommand;
public ICommand NavigateToProductViewModelCommand
{
get
{
if (_navigateToProductViewModelCommand == null)
{
_navigateToProductViewModelCommand = new DelegateCommand<Product>(
(p) =>
{
CurrentViewModel = new ProductViewModel();
});
}
return _navigateToProductViewModelCommand;
}
}
}
TabView.xaml
<UserControl x:Class="Monitoring_Tool.Views.TabView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ProgressBar Value="{Binding Path=CurrentViewModel.PageProgress}" Height="5" Grid.Row="0" Margin="0,0,0,10">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderThickness="0,0,0,0" Background="LightGray" CornerRadius="0" Padding="0">
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#00B6FA" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ProgressBar.Style>
</ProgressBar>
<ContentControl Content="{Binding Path=CurrentViewModel}" Grid.Row="1" />
</Grid>
I instantiate the TabViewModel like this:
new TabViewModel("Producten", new ProductsViewModel())
The ProductsView.xaml is shown like it should be. In the ProductsView.xaml I call the command from the TabViewModel like this:
<DataGrid.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"/>
</DataGrid.InputBindings>
When the datagrid is empty, the command is executed and ProductView.xaml appears like it should be. But when the datagrid is not empty somthing strange happens:
the command is executed, and when I debug I can see that the currentViewModel is changed to ProductViewModel. Then when OnPropertyChanged("CurrentViewModel") is called. There is a set call (value = null) to a depedency property (SelectedAssetCategory) on the ProductsViewModel, wich was replaced and doesn't exits anymore?!
When I put CurrentViewModel = null the same thing happens, I can only do CurrentViewModel = new ProductsViewModel. So I guess it's somthing with updating the UI?
In the App.xaml I defined the following recources:
<DataTemplate DataType="{x:Type viewmodels:TabViewModel}">
<views:TabView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ProductsViewModel}">
<views:ProductsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ProductViewModel}">
<views:ProductView />
</DataTemplate>
The ProductsViewModel looks like this:
class ProductsViewModel: PageViewModel
{
private readonly MonitotingToolEntities _databaseEntities;
public ProductsViewModel()
{
_databaseEntities = new MonitotingToolEntities();
AssetCategories = new ObservableCollection<AssetCategory>(_databaseEntities.AssetCategory.ToList())
{
new AssetCategory() {AssetCategoryID = 0, AssetCategoryName = "Alles"}
};
Results = new ObservableCollection<Product>();
}
public ObservableCollection<AssetCategory> AssetCategories { get; set; }
private AssetCategory _selectedAssetCategory;
public AssetCategory SelectedAssetCategory
{
get { return _selectedAssetCategory; }
set
{
_selectedAssetCategory = value; //this one is called with value = null
OnPropertyChanged("SelectedAssetCategory");
Filter();
}
}
public ObservableCollection<Product> Results { get; set; }
public void Filter()
{
Results.Clear();
List<Product> products =
SelectedAssetCategory.AssetCategoryID == 0
? _databaseEntities.Product.ToList()
: SelectedAssetCategory.Product.ToList();
foreach (Product product in products)
{
Results.Add(product);
}
}
}
ProductsView.xaml:
<UserControl x:Class="Monitoring_Tool.Views.ProductsView"
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:views="clr-namespace:Monitoring_Tool.Views"
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:viewModels="clr-namespace:Monitoring_Tool.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<CollectionViewSource x:Key="CvsAssetCategories" Source="{Binding Path= AssetCategories}" >
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="AssetCategoryID"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="CvsResults" Source="{Binding Path= Results}" >
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="AssetCategory.AssetCategoryName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style TargetType="Image" x:Key="ImageDisabledStyle">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid Margin="0, 0, 0, 10" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Asset Categorie:" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding Source={StaticResource CvsAssetCategories}}"
DisplayMemberPath="AssetCategoryName"
SelectedItem="{Binding SelectedAssetCategory}"
Margin="0,0,10,0"/>
<TextBlock Text="Zoeken:" Grid.Column="3" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox Grid.Column="4"
SelectedItem="{Binding SelectedSearchField}"
Margin="0,0,10,0"/>
<TextBox Text="{Binding Path=SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="5" Margin="0,0,10,0">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=SearchCommand}" CommandParameter="{Binding SearchQuery}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
<Button Grid.Column="6"
Command="{Binding SearchCommand}"
CommandParameter="{Binding SearchQuery}"
Padding="5,0,5,0" Margin="0,0,10,0" >
<Button.Content>
<Image Source="/Recourses/SearchIcon.png"
Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
</Button.Content>
</Button>
<Button Grid.Column="7"
Command="{Binding CancelSearchCommand}"
IsEnabled="{Binding CancelSearchEnabled}"
Padding="5,0,5,0">
<Button.Content>
<Image Source="/Recourses/CancelSearchIcon.png"
Stretch="None" VerticalAlignment="Top" Style="{Binding Source={StaticResource ImageDisabledStyle}}"/>
</Button.Content>
</Button>
</Grid>
<DataGrid Name="DgProducts" AutoGenerateColumns="False"
RowHeaderWidth="0" Margin="0,0,0,10" Grid.Row="1" IsReadOnly="True"
SelectionMode="Single" CanUserReorderColumns="False"
EnableRowVirtualization="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
ItemsSource="{Binding Source={StaticResource CvsResults}}" SelectedItem="{Binding SelectedProduct}">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.InputBindings>
<MouseBinding
MouseAction="LeftDoubleClick"
Command="{Binding DataContext.NavigateToProductViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type views:TabView}}}"
/>
</DataGrid.InputBindings>
<DataGrid.Resources>
<Style TargetType="DataGridColumnHeader" x:Key="DgVerticalColumnHeader">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270" />
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="LightGray"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey }"
Color="Black"/>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" Background="DarkGray" Padding="2,0,0,0"/>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Manager.ManagerName}" Header="Manager" />
<DataGridTextColumn Binding="{Binding Path=ProductName}" Header="Product" />
<DataGridTextColumn Binding="{Binding Path=MonitoringBy}" Header="Monitoring door" />
<DataGridTextColumn Binding="{Binding Path=AumProduct}" Header="AUM Product (mln)" />
<DataGridTextColumn Binding="{Binding Path=AumProductDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Product" />
<DataGridTextColumn Binding="{Binding Path=AumStrategy}" Header="AUM Strategie (mln)" />
<DataGridTextColumn Binding="{Binding Path=AumStrategyDate, StringFormat='{}{0:dd-MM-yyyy}'}" Header="Datum AUM Strategie" />
<DataGridTextColumn Binding="{Binding Path=Aum}" Header="AUM (mln)" />
<DataGridTextColumn Binding="{Binding Path=TotalExpenseRatio}" Header="TER (bp)" />
<DataGridTextColumn Binding="{Binding Path=Fee}" Header="Total Fee" />
</DataGrid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Results.Count, StringFormat='{}{0} Producten'}" Grid.Column="0" Margin="0,0,0,0"/>
<Button Grid.Column="2"
Content="Toevoegen"
Padding="5,0,5,0" Margin="0,0,10,0"
Command="{Binding AddProductCommand}" />
<Button Grid.Column="3"
Content="Verwijderen"
Padding="5,0,5,0"
Command="{Binding Path=RemoveProductCommand}"
CommandParameter="{Binding Path=SelectedProduct}"/>
</Grid>
</Grid>
PageViewModel is an abstract class:
public abstract class PageViewModel: BaseViewModel
{
private int _pageProgress;
public int PageProgress
{
get { return _pageProgress; }
set
{
_pageProgress = value;
OnPropertyChanged("PageProgress");
}
}
}
It's kind of weird but it has something to do with the collectionviewsource (CvsAssetCategories) bound to the combobox. If I bind directly to the combobox without using the collectionviewsource the dependency property is not called. However, I like to use the collectionviewsource for the sortdescriptor. My solution now is a not null check on the setter from the dependency property, but I think its nasty way to go.
Related
I have a ListView where I trying to bind a list of lists.
I grouped the items of mainList based on Key. Below is a sample of XAML and
C# code for binding list to ListView.
I Group MainList by Key. it's OK and working but how can I group second ListView for sublist ?
this is XAML a listView for mainList and another listview in itemtemplate for sublist :
<ListView x:Name="MyList" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Key}" />
<ListView Grid.Row="1" ItemsSource="{Binding SubList}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding id}" />
<TextBlock Grid.Column="1" Text="{Binding name}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="10" Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
</Grid>
<TextBlock Grid.Column="0" Margin="10,0,0,0" Text="Key :" Style="{StaticResource SubHeaderTextBlockStyle}"/>
<TextBlock Grid.Column="1" Margin="10,0,10,0" Text="{Binding Key}" Style="{StaticResource TextBlockStyle}"/>
</StackPanel>
<ItemsPresenter Margin="10" Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
And this is c# :
public MainWindow()
{
InitializeComponent();
ObservableCollection<ListObj> MainList1 = new ObservableCollection<ListObj>
{
new ListObj
{
Key="key1", Value="value1",
SubList=new ObservableCollection<SubListObj>
{
new SubListObj{id="subid1", name="subname1" },
new SubListObj{id="subid2", name="subname2" }
}
},
new ListObj
{
Key="key2", Value="value2",
SubList=new ObservableCollection<SubListObj>
{
new SubListObj{id="subid3", name="subname3" }
}
}
};
MyList.ItemsSource = MainList1;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(MyList.ItemsSource);
PropertyGroupDescription gd = new PropertyGroupDescription("Key");
view.GroupDescriptions.Add(gd);
}
public class ListObj
{
public string Key { get; set; }
public string Value { get; set; }
public ObservableCollection<SubListObj> SubList { get; set; } = new ObservableCollection<SubListObj>();
}
public class SubListObj
{
public string id { get; set; }
public string name { get; set; }
}
You have to do it all in XAML (except the data initialization). Define two CollectionViewSource objects, one for the outer and one for the inner ListView.
To layout the group header use the GroupStyle.HeaderTemplate (instead of GroupStyle.ContainerStyle):
MainWindow.xaml
<Window>
<Window.Resources>
<CollectionViewSource x:Key="MainCollectionViewSource"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:SecondWindow}, Path=MainList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Key" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListView ItemsSource="{Binding Source={StaticResource MainCollectionViewSource}}">
<ListView.ItemTemplate>
<DataTemplate DataType="ListObj">
<Grid>
<Grid.Resources>
<CollectionViewSource x:Key="SubCollectionViewSource"
Source="{Binding SubList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="id" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="{Binding Value}" />
<ListView Grid.Row="1"
ItemsSource="{Binding Source={StaticResource SubCollectionViewSource}}">
<ListView.ItemTemplate>
<DataTemplate DataType="SubListObj">
<Grid>
<TextBlock Text="{Binding name}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate DataType="GroupItem">
<TextBlock FontWeight="Bold"
FontSize="14"
Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate DataType="GroupItem">
<TextBlock FontWeight="Bold"
FontSize="14"
Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ObservableCollection<ListObj> MainList { get; set; }
public MainWindow()
{
InitializeComponent();
MainList1 = new ObservableCollection<ListObj>
{
new ListObj
{
Key="key1", Value="value1",
SubList=new ObservableCollection<SubListObj>
{
new SubListObj{id="subid1", name="subname1" },
new SubListObj{id="subid2", name="subname2" }
}
},
new ListObj
{
Key="key2", Value="value2",
SubList=new ObservableCollection<SubListObj>
{
new SubListObj{id="subid3", name="subname3" }
}
}
};
}
}
I'm grouping the items in listview as shown here. Now I want to bind some of my class properties in the expander header (group header), I'm not able to bind any properties from my class in the <ListView.GroupStyle> section but able to bind properties in <ListView.View>.
Code what I've tried:
<UserControl x:Class="ClientAccessTool.CasesAndTasksDetailsControl"
xmlns:core="clr-namespace:ClientAccessTool.Core;assembly=ClientAccessTool.Core"
DataContext="{x:Static core:SiteListViewModel.Instance}"
>
<UserControl.Resources>
<CollectionViewSource x:Key="groupByCases2" Source="{Binding SelectedSite.CasesAndTasksList}" >
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="CaseName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</UserControl.Resources>
<Grid>
<ListView x:Name="lvCasesAndTasks"
ItemsSource="{Binding Source={StaticResource groupByCases2}}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Margin="3" Width="383" HorizontalAlignment="Left" >
<Expander x:Name="lvExpander" IsExpanded="True"
Style="{StaticResource ExpanderStyle3}" >
<Expander.Header>
<Grid VerticalAlignment="Center" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Viewbox Height="23" Margin="5" Grid.Column="0" DockPanel.Dock="Left">
<Image Source="/Images/RedFlag.png" RenderOptions.BitmapScalingMode="HighQuality"/>
</Viewbox>
<Grid Grid.Column="1" MinWidth="250" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="{Binding Name}"
FontSize="18" VerticalAlignment="Center"
FontWeight="SemiBold"
Foreground="Red"
Margin="5"
/>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5" >
<TextBlock Text="Number of tasks:" FontFamily="{StaticResource FontAwesome}" Foreground="Gray"/>
<TextBlock Margin="5 0 0 0"
Text="{Binding CountOfTasks}"
FontFamily="{StaticResource FontAwesome}" Foreground="Gray" >
</TextBlock>
</StackPanel>
</Grid>
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
c# class:
namespace ClientAccessTool.Core
{
public class Case
{
public string SiteName { get; set; }
public string CaseName { get; set; }
public string CaseDescription { get; set; }
public List<Tasks> ListOfTasks { get; set; } = new List<Tasks>();
public string CountOfTasks => ListOfTasks.Count.ToString() ;
}
}
The item source SelectedSite.CasesAndTasksList is a List of type Case. I want to bind the CountOfTasks property inside the expander header but its not working.
I have done opening the modal dialog in Wpf MVVM. But I am new to prism. I have tried the following code in MVVM. Now its working fine. But I want to do the same concept using prism with ribbon window. In the below code I have not used ribbon window, instead of ribbon window I used the button. please refer the below code,
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:ModalDialogViewModel}">
<view:ModalDialog />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Grid>
<Button
Width="150"
Height="25"
Margin="0,10,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding Path=LoadViewCommand}"
Content="Show Modal Dialog" />
</Grid>
</Grid>
<Grid Visibility="{Binding IsCloseModalWindow, Converter={StaticResource BooleanToVisibilityConverter}}">
<Border Background="#90000000">
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White"
BorderBrush="Transparent"
BorderThickness="0"
CornerRadius="0">
<Border.BitmapEffect>
<DropShadowBitmapEffect
Direction="270"
Opacity="0.5"
ShadowDepth="0.7"
Color="Black" />
</Border.BitmapEffect>
<ContentControl Content="{Binding Path=CurrentViewModel}" />
</Border>
</Border>
</Grid>
</Grid>
MainWindowViewModel
public class MainWindowViewModel : ViewModelBase, ICloseWindow
{
private ICommand loadViewCommand;
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
this.OnPropertyChanged("CurrentViewModel");
}
}
private bool isCloseModalWindow = false;
public bool IsCloseModalWindow
{
get { return isCloseModalWindow; }
set { isCloseModalWindow = value; OnPropertyChanged("IsCloseModalWindow"); }
}
public MainWindowViewModel()
{
}
public ICommand LoadViewCommand => loadViewCommand ?? (loadViewCommand = new RelayCommand(showView, canShowView));
private void showView(object obj)
{
IsCloseModalWindow = true;
CurrentViewModel = new ModalDialogViewModel(new ModalDialogModel() { Name = "New Modal Window" }, this);
}
private bool canShowView(object obj)
{
return true;
}
public void closeWindow()
{
IsCloseModalWindow = false;
}
}
ModalDialog
<Grid MinWidth="300" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
MinHeight="30"
Background="SkyBlue">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Width="40"
Height="25"
Content="X"
BorderThickness="0"
Background="Transparent"
Foreground="White"
Margin="0,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding CancelCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
ToolTip="Close" />
</Grid>
</Grid>
<Grid Grid.Row="1" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
Content="Name"
Style="{StaticResource commonMargin}" />
<Label
Grid.Row="0"
Grid.Column="1"
Content=":"
Style="{StaticResource commonMargin}" />
<TextBox
Grid.Row="0"
Grid.Column="2"
Width="100"
Text="{Binding Path=Name}"
Style="{StaticResource commonTimerMargin}" />
</Grid>
<Grid
Grid.Row="2"
Margin="0,0,15,10"
HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
Width="80"
Height="30"
Margin="0,0,10,0"
Background="White"
Command="{Binding CancelCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
Content="CANCEL" />
<Button
Grid.Column="1"
Width="80"
Height="30"
Background="SkyBlue"
Command="{Binding ApplyCommand}"
CommandParameter="{Binding ElementName=modalDialog}"
Content="APPLY"
Foreground="White" />
</Grid>
</Grid>
ModalDialogViewModel
public class ModalDialogViewModel : ViewModelBase
{
private ICommand cancelCommand;
public ModalDialogModel Model { get; private set; }
private ICloseWindow _closeWindow;
public ModalDialogViewModel(ModalDialogModel modalDialogModel, ICloseWindow closeWindow)
{
this.Model = modalDialogModel;
this._closeWindow = closeWindow;
}
public ICommand CancelCommand => cancelCommand ?? (cancelCommand = new RelayCommand(CloseWindow, CanCloseWindow));
private void CloseWindow(object obj)
{
_closeWindow.closeWindow();
}
private bool CanCloseWindow(object obj)
{
return true;
}
}
My Requirement is when user click the ribbon window button the modal dialog is open. The Shell window is a ribbon window. I have added HomeTab module and many other modules as separate class library.
You can do it like this in .xaml:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:prism="http://prismlibrary.com/"
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding PopUpDialogActionBinding}"> '<-- Here is your action binding
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="False">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="Icon" Value="IconPath"/>
<Setter Property="Height" Value="400"/>
<Setter Property="Width" Value="400"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<views:YourCustomView /> ' <--- Put your view into the dialog
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid>
...
</Grid>
I did it as in the example 28:
https://github.com/PrismLibrary/Prism-Samples-Wpf
I am having troubles grouping RadioButtons when using a WPF form and using DataBinding.
If I manually create my controls the behavior is as expected and the user is only allowed to select one item from the radiobutton group (bottom GroupBox), but if I use nested ItemControls to generate my layout and use data binding grouping does not work as expected and a user is allowed to select multiple radiobuttons in one group (top groupBox) as can be seen in this linked screenshot 1.
I have tried binding the GroupName property but fail to somehow find a binding property to use for GroupName property.
Perhaps the Text property of the TextBlock in the same grid row can be used, but I fail to find the proper Binding syntax to reference it.
What would be the way to get grouping to work?
Here is the XAML:
<Window x:Class="RadioButtonMadness.MainWindow"
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"
mc:Ignorable="d" Title="MainWindow" SizeToContent="WidthAndHeight">
<Window.Resources>
<Style TargetType="RadioButton">
<Setter Property="Margin" Value="10" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Height" Value="22" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Height" Value="22" />
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions />
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Header="Prognostic factors - NOK">
<ItemsControl ItemsSource="{Binding PrognosticFactors}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Name="PrognosticFactors" Grid.Row="0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions />
<TextBlock Text="{Binding Title}" />
<ItemsControl ItemsSource="{Binding Options}" Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Key}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
<GroupBox Header="Prognostic factors - OK" Grid.Row="1">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions/>
<TextBlock Text="Test4" Grid.Column="0"/>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<RadioButton Content="One"/>
<RadioButton Content="Two"/>
<RadioButton Content="Three"/>
</StackPanel>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions />
<TextBlock Text="Test5" Grid.Column="0"/>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<RadioButton Content="One"/>
<RadioButton Content="Two"/>
<RadioButton Content="Three"/>
</StackPanel>
</Grid>
</StackPanel>
</GroupBox>
</Grid>
</Window>
As well as the code behind:
using System;
using System.Collections.Generic;
using System.Windows;
namespace RadioButtonMadness
{
public class Option
{
public String Key{ get; set; }
public Double Value { get; set; }
public Option(String key, Double value)
{
Key = key;
Value = value;
}
}
public class PrognosticFactor
{
private String title;
public List<Option> Options
{
get
{
var list = new List<Option>();
list.Add(new Option("One", 1));
list.Add(new Option("Two", 2));
list.Add(new Option("Three", 3));
return list;
}
}
public String Title
{
get { return title; }
}
public PrognosticFactor(String value)
{
title = value;
}
}
public class ViewModel
{
public List<PrognosticFactor> PrognosticFactors
{
get
{
var list = new List<PrognosticFactor>();
list.Add(new PrognosticFactor("Test1"));
list.Add(new PrognosticFactor("Test2"));
list.Add(new PrognosticFactor("Test3"));
return list;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
}
Just bind GroupName to the Title property. You can do it something like this:
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Key}" GroupName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Path=DataContext.Title}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
I have a ListView (with an inner ListView) that displays data like this:
I would like to display the inner ListView headers above the grouping like so:
Is it possible to re-position the column headers as shown or simply create some fake headers on the outer ListView?
Here is the XAML code I have so far:
<ListView Name="ListView_GarnishmentCalculations"
ItemsSource="{Binding GarnishedEmployees, UpdateSourceTrigger=PropertyChanged}"
MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollContentPresenter}},
Converter={StaticResource MathConverter}, ConverterParameter=x-220}"
Margin="5,20,10,10"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4">
<!-- Required for right justifying text in a TextBlock -->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<!-- Group results and show EmpNo, Name and WorkState -->
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="175" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Items[0].EmpNo}"
FontWeight="Bold"
Grid.Column="0" />
<TextBlock Text="{Binding Items[0].FullName}"
FontWeight="Bold"
Grid.Column="1" />
<TextBlock Text="{Binding Items[0].WorkState}"
FontWeight="Bold"
Grid.Column="2" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<!-- Inner ListView of garnishment details -->
<ListView ItemsSource="{Binding Garnishments}">
<ListView.View>
<GridView>
<!-- CaseID -->
<GridViewColumn Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CaseNumber, Converter={StaticResource StringIsNullOrEmptyConverter}, ConverterParameter='No Case ID'}"
TextAlignment="Left">
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader Content=" Case ID" />
</GridViewColumn.Header>
</GridViewColumn>
<!-- Vendor -->
<GridViewColumn Width="150"
DisplayMemberBinding="{Binding Vendor}">
<GridViewColumn.Header>
<GridViewColumnHeader Content=" Vendor" />
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is what I came up with, NOT by my self I used my google fu skills for this.
Credit to this SO post.
So here is what I have for my model:
namespace Model
{
public class Case
{
public int CaseID { get; set; }
public int Vendor { get; set; }
}
}
And now user:
namespace Model
{
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public string State { get; set; }
public List<Case> Cases { get; set; }
}
}
Now in my MainViewModel:
using Model;
using System.Collections.Generic;
namespace VM
{
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
Users = new List<User>();
for (int i = 0; i < 20000; i++)
{
Users.Add(new User
{
ID = i,
Name = $"John the {i + 1}",
State = i % 2 == 0 ? "CA" : "IL",
Cases = new List<Case>() { new Case { CaseID = (i + 1) * 10, Vendor = ((i + 1) * 10) - 2 }, new Case { CaseID = (i + 1) * 10, Vendor = ((i + 1) * 10) - 2 } }
});
}
}
private List<User> users;
public List<User> Users
{
get { return users; }
set { users = value; OnPropertyChanged(); }
}
}
}
On to the View:
<Window x:Class="SO_App.MainWindow"
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:vm="clr-namespace:VM;assembly=VM"
xmlns:model="clr-namespace:Model;assembly=Model"
xmlns:local="clr-namespace:SO_App"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<CollectionViewSource Source="{Binding Users}" x:Key="Users"/>
</Window.Resources>
<Grid>
<ListView>
<ListView.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource Users}}"/>
</CompositeCollection>
</ListView.ItemsSource>
<ListView.View>
<GridView>
<GridViewColumn Header="Case ID" Width="100"/>
<GridViewColumn Header="Vendor" Width="100"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding ID}" MinWidth="50"/>
<TextBlock Text="{Binding Name}" MinWidth="250" Grid.Column="1"/>
<TextBlock Text="{Binding State}" MinWidth="50" Grid.Column="2"/>
<ListView Grid.Row="1" ItemsSource="{Binding Cases}" Grid.ColumnSpan="3">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding CaseID}" MinWidth="100"/>
<TextBlock Text="{Binding Vendor}" MinWidth="100" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Caveat:
You will need to handle the scroll event on the inner ListView so it doesn't swallow the mouse scroll.
P.S.
this is the BaseViewModel implementation:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace VM
{
public class BaseViewModel : INotifyPropertyChanged
{
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string prop = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
#endregion
}
}
Which then produces this as a result: