Control events inside interaction pop up not working - popup

I have an interaction request pop up and inside that pop up, I have few controls like time picker, radcombobox and so on. I want change events of these controls hit in my view model class using prism commands.
my sample code is not working, time picket value change is not hitting on OnReasonTimeChanged Method.
XAML file
<i:Interaction.Triggers>
<i:EventTrigger SourceName=" " EventName="ValueChanged">
<i:InvokeCommandAction Command="{Binding ReasonTimeChanged}" />
</i:EventTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding SummaryConfirmationInteractionRequest}">
<prism:PopupChildWindowAction>
<prism:PopupChildWindowAction.ContentTemplate>
<DataTemplate>
<StackPanel Width="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" Height="Auto"
MaxHeight="400" >
<ScrollViewer BorderThickness="1" VerticalScrollBarVisibility="Auto" Width="820" Height="Auto" MaxHeight="200">
<StackPanel Height="Auto" >
<controls:XRadGridView x:Name="UnitsGrid" ItemsSource="{Binding Units, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
MaxHeight="300"
IsReadOnly="True"
RowIndicatorVisibility="Collapsed"
CanUserInsertRows="False"
CanUserDeleteRows="False"
ShowColumnHeaders="False"
ShowGroupPanel="False"
CanUserResizeColumns="False"
CanUserReorderColumns="False"
AutoGenerateColumns="False">
<controls:XRadGridView.Columns>
<telerik:GridViewDataColumn Header="DUID"
DataMemberBinding="{Binding DisplayDuid}"
IsReadOnly="True"
Width="Auto" />
<telerik:GridViewDataColumn Header="ReasonTime">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<toolkit:TimePicker x:Name="ReasonTime"
VerticalAlignment="Center" HorizontalAlignment="Center" Width="20"
MaxWidth="20"
Loaded="ReasonTime_Loaded"
Format="HHmm"
Value="{Binding OfferHeader.ReasonTime, Mode=TwoWay}"
controls:TimePickerExtensions.UpdateBindingOnValueChanged="{Binding ApplyReasonToAll}"
TabIndex="0"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
</controls:XRadGridView.Columns>
</controls:XRadGridView>
ViewModel code
Inside constructor I have below code
_reasonTimeChangedCommand = new DelegateCommand(OnReasonTimeChanged);
command details
private readonly DelegateCommand _reasonTimeChangedCommand;
public ICommand ReasonTimeChanged => _reasonTimeChangedCommand;
public void OnReasonTimeChanged()
{
}

The InvokeCommandAction should be inside the popup, to be used within the popup... but you bind to the value to a property (ReasonTime), and that property should be responsible for notifying the change. That's the view model's job, not just being a container for commands or forwarding properties from some DTO.

Related

How can I make a list view have alternating background colors? NET MAUI

I am trying to create a list view where the background color alternates for each entry in the list. Is there a way to do this in MAUI?
This can be done in multiple ways.And the method often recommended is through a DataTemplateSelector.
1.Create a DataTemplateSelector that holds two templates and selects them based on the index of the item:
public class AlternateColorDataTemplateSelector: DataTemplateSelector
{
public DataTemplate EvenTemplate { get; set; }
public DataTemplate UnevenTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
// TODO: cast `Monkey` to your Item
return ((List<Monkey>)((ListView)container).ItemsSource).IndexOf(item as Monkey) % 2 == 0 ? EvenTemplate : UnevenTemplate;
}
}
2.In XAML, we can define two templates, one with the alternate color and one with the normal color.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp0606.MainPage"
xmlns:local="clr-namespace:MauiApp0606"
>
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="evenTemplate">
<ViewCell>
<Grid BackgroundColor="White">
<Label Text="{Binding Name}" HorizontalOptions="Center" VerticalOptions="Center" />
</Grid>
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="unevenTemplate">
<ViewCell>
<Grid BackgroundColor="LightGray">
<Label Text="{Binding Name}" TextColor="White" HorizontalOptions="Center" VerticalOptions="Center" />
</Grid>
</ViewCell>
</DataTemplate>
<local:AlternateColorDataTemplateSelector x:Key="alternateColorDataTemplateSelector"
EvenTemplate="{StaticResource evenTemplate}"
UnevenTemplate="{StaticResource unevenTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout Spacing="25" Padding="30">
<ListView ItemsSource="{Binding Monkeys}" ItemTemplate="{StaticResource alternateColorDataTemplateSelector}">
</ListView>
</VerticalStackLayout>
</ContentPage>
To solve this problem I added a LineNumber property to my view model and used a value converter to set the color.
The value converter:
public class NumberToAlternatingColorValueConverter : IValueConverter
{
public Color EvenNumberColor { get; init; } = Colors.Green;
public Color OddNumberColor { get; init; } = Colors.Red;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is not int intValue
? Colors.Transparent
: (intValue % 2) == 0
? EvenNumberColor
: OddNumberColor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("NumberToAlternatingColorValueConverter.ConvertBack");
}
}
Register the value converter
I registered the value converter in my main resource dictionary like so:
<converter:NumberToAlternatingColourValueConverter
x:Key="MagentaCyanRowColorConverter"
EvenNumberColour="Magenta"
OddNumberColour="Cyan" />
You can register more than one value converter with a different key for different colour combinations.
Use the value converter
And I used the bound value converter in my XAML like so:
<StackLayout BindableLayout.ItemsSource="{Binding Summary.Lines}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="vm:SummaryLineViewModel">
<Grid BackgroundColor="{Binding LineNumber, Converter={StaticResource MagentaCyanRowColorConverter}}">
<Label Grid.Column="0" Text="{Binding Name}" />
<Label Grid.Column="1" Text="{Binding Value}" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
As in this answer we can used DataTemplateSelector to resolve your problem.
Alternative solutions,
You can use a property in the model and straight a way bind it to the row background. (In your case Grid's Background color)
You can use triggers make the color change by considering a logic.
( Xamarin Forms Triggers )

How to apply view-side filtering in MVVM?

I'm using Telerik RadGridView in my project (which essentially is a standart GridView).
This component has its own filtering functionality and I want to get advantage of it.
Filtering itself I am planning to do based on several combobox selected values. So if I got right idea of MVVM, I need to bind the combos to some ViewModel's properties. But here's a problem of how to pass these selected values back to View's component? how to make it refresh filtering as selected value change?
upd: I use SimpleMVVM framework.
XAML of MainWindow:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
......
DataContext="{Binding Source={StaticResource Locator}, Path=MainPageViewModel}">
<StackPanel Height="auto">
<telerik:RadMenu VerticalAlignment="Top">
......
</telerik:RadMenu>
<my:Expander VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="auto" Width="auto"/>
<my:CustomerView Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="auto" Width="auto"/>
</StackPanel>
XAML of expander:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...... >
<Grid Margin="10,10,0,10" Width="684" Height="97" VerticalAlignment="Top" HorizontalAlignment="Left" >
<telerik:RadExpander x:Name="radExpander" IsExpanded="True" HorizontalAlignment="Stretch" VerticalAlignment="Top" telerik:AnimationManager.IsAnimationEnabled="True" Margin="0,0,0,0" Grid.RowSpan="2">
<telerik:RadComboBox HorizontalAlignment="Left" Margin="244,-2,0,0" VerticalAlignment="Top" Width="154" Height="26"
ItemsSource="{Binding Path=AllLevels}" DisplayMemberPath="name" SelectedItem="{Binding SelectedEventLevel}"/>
.......
</Grid>
</telerik:RadExpander>
</Grid>
XAML of CustomerView:
<telerik1:RadGridView Name="EventList" .... ItemsSource="{Binding SportEventsList}" AutoGenerateColumns="False">
<telerik1:RadGridView.Columns>
.....
</telerik1:RadGridView.Columns>
</telerik1:RadGridView>
Snippet of Viewmodel's code:
private ObservableCollection<sportevent> _sportEventsList;
public ObservableCollection<sportevent> SportEventsList
{
get { return _sportEventsList; }
set
{
_sportEventsList = value;
NotifyPropertyChanged(vm => vm.SportEventsList);
}
}
Add the following properties to your VM:
private ObservableCollection<yourType> allLevels;
public ObservableCollection<yourType> AllLevels
{
get
{
return allLevels;
}
set
{
allLevels = value;
RaisePropertyChanged("AllLevels");
}
}
private yourType selectedEventLevel;
public yourTypeSelectedEventLevel
{
get
{
return selectedEventLevel;
}
set
{
selectedEventLevel = value;
RaisePropertyChanged("SelectedEventLevel");
}
}
I assume MainPageViewModel inherits SimpleViewModelBase

SilverLight 4.0 C# - DomainDataSource DataGrid with CheckBox Column (UI only - not a data field) to allow user to select multiple records/rows

New to SilverLight and to posting here. Please have mercy and be specific :)
Using RIA services with DomainDataSource and DataGrid control to display data rows from SQL server query
Goal: Have checkbox column (UI only - not a data field) to allow user to select multiple records/rows
BackGround:
1) Created new SilverLight 4, C# solution with RIA services
2) in ProjectName.Web
Created Entity Framework (EF) model referencing SQL server table/view (built solution).
Created Domain Sevice using EF model (built solution).
3) in SilverLightProjectName
From Data Sources window, dragged table onto a design surface to create DomainDataSource and DataGrid (this works great to bind DataGrid to data source)
4) in MainPage.XAML added checkbox column
What's Happening: checkboxes are selected/checked by user, scroll down, scroll back up, all checkboxes reset and ONLY Datagrid.SelectedItem is still checked. I have read this behavior is 'by design' due to paging.
<sdk:DataGrid RowStyle="{StaticResource newDataGridStyle}" AutoGenerateColumns="False" ItemsSource="{Binding ElementName=ddsPagerApp, Path=Data}" Name="vwPagerAppDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" MouseLeftButtonDown="vwPagerAppDataGrid_MouseLeftButtonDown" VerticalGridLinesBrush="#FFB9B9B9" FontSize="10" Grid.Row="3" SelectionChanged="vwPagerAppDataGrid_SelectionChanged" KeyDown="vwPagerAppDataGrid_KeyDown" MouseLeftButtonUp="vwPagerAppDataGrid_MouseLeftButtonUp" MouseRightButtonUp="vwPagerAppDataGrid_MouseRightButtonUp" DataContext="{Binding}" SelectionMode="Single" IsEnabled="True" IsReadOnly="False" TabIndex="2" Grid.Column="1" Margin="0,10,9,9">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn IsReadOnly="False">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="ChkSelected" IsThreeState="False" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" Unchecked="IndividualCheckBox_Unchecked" Checked="IndividualCheckBox_Checked" Width="Auto" Height="Auto" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTextColumn x:Name="fullNameColumn" Binding="{Binding Path=FullName}" Header="Full Name" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="departmentColumn" Binding="{Binding Path=department}" Header="Department" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="pager_number_displayColumn" Binding="{Binding Path=pager_number_display}" Header="Pager Number" IsReadOnly="True" />
<sdk:DataGridTextColumn x:Name="PageTo" Binding="{Binding Path=PageTo}" Header="Page To" IsReadOnly="True" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:vwPagerApp, CreateList=true}" Height="0" LoadedData="ddsPagerApp_LoadedData" Name="ddsPagerApp" QueryName="GetVwPagerAppsQuery" Width="0" Margin="10,0,25,45" Background="#FF7D0000" Foreground="#FF7D0000" Visibility="Visible">
<riaControls:DomainDataSource.DomainContext>
<my:NotifyDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
Attempt 1:
in EFModel.edmx, added Boolean Scalar Property 'IsChecked'
in DomainService.metadata.cs, added public bool IsChecked { get; set; }
in MainPage.XAML. added (above) IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"
Getting error: Error 11009: Property ' ' is not mapped
UPDATE: Reversed Attempt 1:
Attempt 2:
Researching possibility of defining a partial class for the entity, wiring to DataGrid, and using that to track CheckBox values. Any advice on if this will work/how to?
Trying my best to absorb this. Please enlighten me...and thank you in advance :)
This is what I implemented and it worked beautifully. I hope it helps someone else in the future :)
1) SilverLight Project C#: Extend entity class using partial class (in separate file, but within same namespace – compiles into 1 class but keeps generated code separate)
namespace Pager.Web
{
public partial class pager_grp
{
bool _IsChecked = false;
public bool IsChecked
{
get
{return this._IsChecked;}
set
{this._IsChecked = !IsChecked;}
}
}
}
2) SilverLight Project GUI: Create Column in DataGrid of type DataGridTemplateColumn named chkSelected
3) SilverLight Project XAML: use template and bind to new property declared in partial class
<sdk:DataGridTemplateColumn IsReadOnly="False">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<CheckBox Name="ChkSelected" IsThreeState="False" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" Unchecked="IndividualCheckBox_Unchecked" Checked="IndividualCheckBox_Checked" Width="Auto" Height="Auto" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>

SketchFlow / Using (Selected) TabItem in TabControl to Trigger State

Context:
A new Sketchflow / Silverlight project.
Expression Blend 4 (Ultimate)
I have the below code.
I have a TabControl with 2 TabItem's.
I also have 2 "callout" (big bubble things) as quick visual on the state change.
I created a VisualStateGroup and added to States under it.
When I manually invoke these from the NAVIGATE window (after I run the project), the states work as expected. callout1 and callout2 flip flop their opacities (between 100% and 10%).
So I have a basic understanding of State's and how they work.
However, when I add a trigger event to the TabItem, the trigger looks good, but does not work.
Below is a stripped example, down to the bare bones.
I tried
EventName="MouseLeftButtonDown"
and
EventName="Click"
with no luck.
I also commented out the ObjectAnimationUsingKeyFrames tags, no luck there as well.
Anybody see what I'm missing?
Basically, I cannot get (selecting) a TabItem to trigger a State change.
Thanks.
-----------START XAML CODE
<UserControl
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:data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:pi="http://schemas.microsoft.com/prototyping/2010/interactivity"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Class="MyProject.MyScreen"
Width="640" Height="480" mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisibleTabs">
<VisualState x:Name="Tab1VisualState">
<Storyboard>
<DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout1" d:IsOptimized="True"/>
<DoubleAnimation Duration="0" To="0.1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout2" d:IsOptimized="True"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(TabControl.SelectedIndex)" Storyboard.TargetName="tabControl">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Int32>0</System:Int32>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Tab2VisualState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(TabControl.SelectedIndex)" Storyboard.TargetName="tabControl">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<System:Int32>1</System:Int32>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="0.1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout1" d:IsOptimized="True"/>
<DoubleAnimation Duration="0" To="1.0" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="callout2" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<data:TabControl x:Name="tabControl" Height="150" Margin="41,0,215,50" VerticalAlignment="Bottom" SelectedIndex="0">
<data:TabItem Header="Tab Number One" Height="24" VerticalAlignment="Bottom">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<pi:ActivateStateAction TargetState="Tab1VisualState"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</data:TabItem>
<data:TabItem Header="Tab Number Two">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<pi:ActivateStateAction TargetState="Tab2VisualState"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</data:TabItem>
</data:TabControl>
<ed:Callout x:Name="callout1" AnchorPoint="0,1.25" CalloutStyle="Oval" Content="Tab1 Rocks" Foreground="{StaticResource BaseForeground-Sketch}" Fill="{StaticResource BaseBackground-Sketch}" FontSize="{StaticResource SizeDouble-Sketch}" FontFamily="{StaticResource FontFamily-Sketch}" ed:GeometryEffect.GeometryEffect="Sketch" HorizontalAlignment="Left" Height="100" Margin="0,84,0,0" Stroke="{StaticResource BaseBorder-Sketch}" StrokeThickness="2" VerticalAlignment="Top" Width="200" Opacity="1.0"/>
<ed:Callout x:Name="callout2" AnchorPoint="0,1.25" CalloutStyle="Oval" Content="Tab2 Rocks" Foreground="{StaticResource BaseForeground-Sketch}" Fill="{StaticResource BaseBackground-Sketch}" FontSize="{StaticResource SizeDouble-Sketch}" FontFamily="{StaticResource FontFamily-Sketch}" ed:GeometryEffect.GeometryEffect="Sketch" HorizontalAlignment="Left" Height="100" Margin="200,84,0,0" Stroke="{StaticResource BaseBorder-Sketch}" StrokeThickness="2" VerticalAlignment="Top" Width="200" Opacity="0.1"/>
</Grid>
</UserControl>
Here is a simple trigger you can use to trigger actions based on tab selection. Add this to your project, compile, and set the trigger of the behvior to an instance of this trigger type. The behavior has to be attached to the TabControl itself. Then set the TabIndex of the trigger to the index you want to trigger the action. The trigger listens to the selection changed event of the tabcontrol and matches that against the TabIndex value you supply.
public class TabSelectedTrigger : TriggerBase<TabControl>
{
public static readonly DependencyProperty TabIndexProperty = DependencyProperty.Register("TabIndex", typeof (int),
typeof (TabSelectedTrigger),
new PropertyMetadata(-1));
public int TabIndex
{
get { return (int)this.GetValue(TabIndexProperty); }
set { this.SetValue(TabIndexProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(this.TabIndex == this.AssociatedObject.SelectedIndex)
{
this.InvokeActions(null);
}
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
}
}
Example usage:
<sdk:TabControl Margin="59,49,67,81">
<i:Interaction.Triggers>
<local:TabSelectedTrigger TabIndex="1">
<ei:GoToStateAction StateName="VisualState1"/>
</local:TabSelectedTrigger>
<local:TabSelectedTrigger TabIndex="0">
<ei:GoToStateAction StateName="VisualState"/>
</local:TabSelectedTrigger>
</i:Interaction.Triggers>
<sdk:TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</sdk:TabItem>
<sdk:TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</sdk:TabItem>
</sdk:TabControl>
A few ideas but nothing definite:
It may be that the stateActions need to be told about the location of the visualstates (note there should be the usual < /> around this code but the code will not show up at all if I include these)
pi:ActivateStateAction TargetState="Tab2VisualState" TargetObject="{Binding ElementName=LayoutRoot}"
you could try a GoToStateAction instead (I know this is the Blend way and not sketchflow but they are very similar)
try a ControlStoryBoardAction as the trigger, for this you will need to name your storyboards.
If none of the above work this may help to narrow down the issue but fundamentally your code looks sound.

Collapse/Visible UserControls on ButtonClick with MVVM - no swap mechanism -

In my scenario I have a MainView + MainViewModel, UserControl1 + UserControl 2.
In the MainView I have 2 buttons labeled: Button_ShowUserControl1 + Button_ShowUserControl2.
At the lower part of the MainView I have a "ContentGrid" which takes/should_take... every UserControl.
My goal:
When Button_ShowUserControl1 is clicked UserControl1 is Visible and UserControl2 OR any other UserControl must be set to Collapsed. Same is valid for Button_ShowUserControl2.
My problem:
1.) As the UserControls shall be loaded at application start how can I put them all together in the one "ContentGrid"? Thats actually not possible... so how can I make one UserControl visible while the other is in the same place/"ContentGrid" just collapsed ?
2.) As 1.) seems not possible how can I instantiate all UserControls at start of the application and make them only Visible/Collapsed when respective Button is clicked?
3.) As a UserControl has a property Visibility = Visible/Hidden/Collapsed, how can I bind to a property in a ViewModel return such a value like Collapsed? I only could get a boolean value like Visibility = false/true ?
My testcode:
<Grid x:Name="LayoutRoot" Background="#FFBDF5BD" ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="96*" />
<RowDefinition Height="289*" />
</Grid.RowDefinitions>
<Grid HorizontalAlignment="Stretch" Name="MenuGrid" VerticalAlignment="Stretch" Background="#FFCECEFF">
<StackPanel Name="stackPanel1" Background="#FFEDFF00" Orientation="Horizontal">
<Button Content="User Data 1" Height="35" Name="button1" Command="{Binding Path=ShowUserControl1Command}" Width="150" Margin="100,0,0,0" />
<Button Content="User Data 2" Height="35" Name="button2" Width="150" Margin="100,0,0,0" />
</StackPanel>
</Grid>
<Grid Grid.Row="1" HorizontalAlignment="Stretch" Name="ContentGrid" VerticalAlignment="Stretch" Background="#FFB15454" />
</Grid>
<UserControl x:Class="SwapUserControls.MVVM.UserControl2"
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:vm="clr-namespace:SwapUserControls.MVVM.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Visibility="{Binding IsUserControl1Collapsed, Path=Value}">
<UserControl.Resources>
<vm:MainViewModel x:Key="MainViewModelID" />
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource MainViewModelID}" />
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="228*" />
<RowDefinition Height="72*" />
</Grid.RowDefinitions>
<Button Content="UserControl2" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="112,27,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<DataGrid HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Stretch" Background="#FFC046F8" />
</Grid>
public class MainViewModel : ViewModelBase
{
RelayCommand _ShowUserControl1Command;
private bool _IsUserControl1Collapsed;
public RelayCommand ShowUserControl1Command
{
get
{
if (_ShowUserControl1Command == null)
{
_ShowUserControl1Command = new RelayCommand( () => ShowUserControl1() );
}
return _ShowUserControl1Command;
}
}
public void ShowUserControl1()
{
_IsUserControl1Collapsed = true;
}
public bool IsUserControl1Collapsed
{
get
{
return _IsUserControl1Collapsed;
}
}
}
Yes the code is wrong, therefore I ask here :)
You only have 2 things wrong with this code.
1) You can't set the visibility of a usercontrol directly... you have to set it on a container:
<Grid Visibility="Collapsed">
<myControls:MyUserControl />
</Grid>
2) Visibility is not a boolean value, it is an enum. As such, you will need to use a converter to convert from boolean to Visibility. Observe:
<Window ...>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>
<Grid Visibility="{Binding ShouldShowUsercontrol1, Converter={StaticResource BoolToVis}}">
<myControls:MyUserControl />
</Grid>
</Window>
That should be it. Hope this helps.
There are other things that you are leaving clues about that might affect the ability of this to work. For example, you don't show the biggest container element... are you wrapping everything in a StackPanel? If you are wrapping everything in a Grid, for example, the controls will overlay everything in layers.
Try these changes I suggest... it should get you closer.
Edit: Another idea using data templates
Another thing you could do is make sure you have a unique ViewModel for each of these views you want to show and hide:
public class MyFirstViewModel : ViewModel
{
}
public class MySecondViewModel : ViewModel
{
}
Then from your "parent" or "main" ViewModel, you show or hide the views you want by virtue of having them in a collection of ViewModels:
public MyMainViewModel : ViewModel
{
public ObservableCollection<ViewModel> ViewsToShow
{
...
}
public void ShowFirstViewModel()
{
ViewsToShow.Add(new MyFirstViewModel());
}
}
To wire everything up in your view, you would then datatemplate these types with their user controls (but this would not cause those views to be instantiated unless they were needed:
<Window ...>
<Window.Resources>
<DataTemplate DataType="{x:Type myViewModels:MyFirstViewModel}">
<myViews:MyFirstView />
</DataTemplate>
<DataTemplate DataType="{x:Type myViewModels:MySecondViewModel}">
<myViews:MySecondView />
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding ViewsToShow}" />
</Window>
And for any ViewModels you put in "ViewsToShow", the view will automatically see that and template in the appropriate view. Again, without instantiating it before it's needed.
This is probably a little cleaner than putting everything single thing in the View and setting visibility, but it would be dependent on you have a unique view model type for every view, which might not be the case.
The question of saving state comes up when using the DataTemplated approach. The solution here is to tread your ViewModel as the state of the control and design both your ViewModels and your Views accordingly. Here is an example that allows you to swap out your Views using DataTemplating, but switching back and forth saves state.
Assume you have the setup from the last section w/ 2 viewmodels that have datatemplates defined. Let's change up the MainViewModel a little:
public MyMainViewModel : ViewModel
{
public RelayCommand SwapViewsCommand
{
...
}
public ViewModel View
{
...
}
private ViewModel _hiddenView;
public MyMainViewModel()
{
View = new MyFirstViewModel();
_hiddenView = new MySecondViewModel();
SwapViewsCommand = new RelayCommand(SwapViewModels);
}
public void SwapViewModels()
{
var hidden = _hiddenView;
_hiddenView = View;
View = hidden;
}
}
And a few changes to the main view. I've omitted the DataTemplates for brevity.
<Window ...>
<!-- DataTemplates Here -->
<Button Command="{Binding SwapViewsCommand}">Swap!</Button>
<ContentControl Content="{Binding View}" />
</Window>
That's it. The secret here is I'm saving the reference to the original view model. This way, let's say there is a string property in a viewmodel and an associated textbox in the DataTemplated usercontrol with a two-way binding then the state will essentially be saved.