Swiching the Stackpanel in single ViewModel Using Back/Next Button WPF - wpf

I have a WPF, with Form with multiple user required questions. I have created 2 Stackpanels which have similar layout, first form (Stackpanel) will let the user to fill up information.While second form (Stackpanel) would be preview form for the user before he submits.
XAML Code
<Window x:Class="NinjaLIB.View.SSOEMWarrantyButtonExtension"
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:local="clr-namespace:NinjaLIB.View"
xmlns:lex="http://wpflocalizeextension.codeplex.com"
lex:LocalizeDictionary.DesignCulture="en"
lex:ResxLocalizationProvider.DefaultAssembly="NinjaLIB"
lex:ResxLocalizationProvider.DefaultDictionary="Resources"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
DataContext="{Binding Path=RMAView, Source={StaticResource Locator}}"
mc:Ignorable="d"
Title="Request RMA" Height="600" Width="1000"
xmlns:mvvmlight="http://www.galasoft.ch/mvvmlight"
xmlns:converter="clr-namespace:NinjaLIB.Converter"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<i:Interaction.Triggers>
<!--<i:EventTrigger EventName="Closed">
<mvvmlight:EventToCommand Command="{Binding GetRMAWindowClosed}" PassEventArgsToCommand="True" />
</i:EventTrigger>-->
<!--<i:EventTrigger EventName="Loaded">
<mvvmlight:EventToCommand Command="{Binding GetWarrantyWindowLoaded}" PassEventArgsToCommand="True" />
</i:EventTrigger>-->
</i:Interaction.Triggers>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--Some QUestions asked to the User-->
<Grid Grid.Row="2">
<!--View Form Fill-->
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewState}" Value="FormFillViewState">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
**<!--Some QUestions asked to the User-->**
</StackPanel >
</ScrollViewer>
**<!--View Preview Form-->**
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewState}" Value = "PreviewFillState" >
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
**<!--Some QUestions asked to the User-->**
</StackPanel>
</ScrollViewer>
</Grid>
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--Buttons-->
<Button Grid.Row="0" Content="Next"
IsEnabled="{Binding IsConnected}"
Command="{Binding NextCommandBtn}"
FontWeight="Normal"
HorizontalContentAlignment="Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Height="30"
Width="80"
Margin="780,0,0,2"
Padding="3,0"/>
<Button Grid.Row="0" Content="Back"
IsEnabled="{Binding IsConnected}"
Command="{Binding BackCommandBtn}"
FontWeight="Normal"
HorizontalContentAlignment="Center"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Height="30"
Width="80"
Margin="420,0,0,2"
Padding="3,0"/>
</Window>
==============
View Model Section Code
==============
private RMAViewModel RMAView;
private readonly ResourceManager rm;
public RMAViewModel()
{
rm = new ResourceManager("NinjaLIB.Properties.Resources", Assembly.GetExecutingAssembly());
}
//Properties for the Window (Date and Text) for FormFillViewState
//Properties for the Window (Date and Text) for PreviewFillState
==============
I wanted some help with view Model code, where if I click NextCommandBtn all the data will appear as filled up form from first stackpanel view and when I select BackCommandBtn i can edit the form.

You can think about it like this:
You have two views (StackPanels) and only one can be displayed at a time.
So in your view-model, you need a property that tells you which view is active. A simple bool property for each view would work. Since there are only two views, you could technically just use a single property, but using two will make your XAML data binding a little easier.
Examples:
IsFormActive
IsPreviewActive
In your XAML, you need to control the Visibility property of your two StackPanel controls based on your IsFormActive and IsPreviewActive properties in your view-model. You can do this with a value converter, specifically the BooleanToVisibilityConverter.
<StackPanel Visibility="{Binding IsFormActive,
Converter={StaticResource BooleanToVisibilityConverter}">
...
</StackPanel>
<StackPanel Visibility="{Binding IsPreviewActive,
Converter={StaticResource BooleanToVisibilityConverter}">
...
</StackPanel>
And back in your view-model you can control the state of IsFormActive and IsPreviewActive when the next and back buttons are clicked, etc.
I hope this helps you get an idea of how to proceed.

Related

WPF Can't Remove Red Border from UserControl

I've built a simple login page and when invalid input is detected, a red border is drawn around the usercontrol.
Here is my layout code:
<UserControl x:Class="WPFTest.UI.Views.EmptyLayout"
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:local="clr-namespace:WPFTest.UI.Views"
xmlns:vm="clr-namespace:WPFTest.UI.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance Type=vm:EmptyLayoutViewModel}"
Background="White"
>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" FontSize="20" FontWeight="Bold">Test Sales Estimator</TextBlock>
<ContentControl Content="{Binding Layout}" Grid.Row="1">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<local:LoginView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</UserControl>
And here is the login view:
<UserControl
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:local="clr-namespace:WPFTest.UI.Views" xmlns:viewmodels="clr-namespace:WPFTest.UI.ViewModels"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" x:Class="WPFTest.UI.Views.LoginView"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Background="White"
d:DataContext="{d:DesignInstance Type={x:Type viewmodels:LoginViewModel}}"
Margin="20 0 0 0"
Padding="10"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="2" Margin="0 50 0 0">
<TextBlock Text="Welcome" FontSize="17" Grid.Row="1" Margin="0 0 0 20" Height="50"/>
<StackPanel>
<TextBlock Text="Username" />
<TextBox Text="{Binding Username}"/>
</StackPanel>
<StackPanel Grid.Row="2" Margin="0 10 0 0">
<TextBlock Text="Password" />
<PasswordBox x:Name="Password" PasswordChanged="PasswordBox_PasswordChanged" >
</PasswordBox>
</StackPanel>
<Button
Grid.Row="2"
Margin="0 20 0 0"
Padding="5 2"
HorizontalAlignment="Left"
Command="{Binding HandleLoginCommand}"
IsEnabled="{Binding CanLogin}"
Content="Login">
</Button>
</StackPanel>
</Grid>
</UserControl>
I have tried to override the border with the following template:
<Style TargetType="views:LoginView">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Blue"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
But the border remains red. I've also tried changing the target on the template to things like UserControl, and ContentControl. I've also tried setting the Validation.ErrorTemplate attribute to {x:Null} on both the UserControl and on the element inside the layout usercontrol.
For the LoginViewModel, I am using the CommunityToolkit.Mvvm's ObservableValidator as my base class, so it handles the validation logic and here is my Username property.
private string _username;
[Required]
[MinLength(4)]
public string Username
{
get { return _username; }
set {
SetProperty(ref _username, value, true);
OnPropertyChanged(nameof(HandleLoginCommand));
}
}
The border is from your contentcontrol.
Set Validation.ErrorTemplate="{x:Null}" on that.
You can just use a contentpresenter.
<ContentPresenter Content="{Binding Layout}" Grid.Row="1"
Validation.ErrorTemplate="{x:Null}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:LoginViewModel}">
<local:LoginView/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
I have a simplified layout to explore this, but the outer red border does not appear when I make that change.
The validation decoration does not use the control's Border(Thcikness/Brush). It is instead a separate Visual that is drawn in a separate AdornerLayer. To modify its looks, you should pass a dedicated template for the ErrorTemplate like this: (example for TextBox)
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderBrush="Yellow" BorderThickness="3">
<AdornedElementPlaceholder x:Name="AdornedElementPlaceholder" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To actually remove the border, try setting Validation.ErrorTemplate to {x:Null}.

How to show the scrollbar in expander when content overflows the window

I'm using two expander with TextBox one after another. During writing text the TextBox dynamically changes height. When size of TextBox is higher then parent window the scrollbar isn't shown. Here is example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="150" Width="150">
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*" Name="GridRow1"></RowDefinition>
<RowDefinition Height="*" Name="GridRow2"></RowDefinition>
</Grid.RowDefinitions>
<Expander Grid.Row="0">
<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
</Expander>
<Expander Grid.Row="1">
<TextBox TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"/>
</Expander>
</Grid>
</Window>
I need to set max height of expander to half size of parent window (window is resizable). The scrollbar should be displayed if the text is longer than half size of the window. Other, when both expander are closed they should be close to each other on the top.
Scrollbar works well when in row definition is asterisk (*) but closed expander aren't not together at the top.
Apply your requirement in a Style DataTrigger for RowDefinition
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Name="GridRow1">
<RowDefinition.Style>
<Style TargetType="{x:Type RowDefinition}">
<Setter Property="Height"
Value="*" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=expanderOne,
Path=IsExpanded}"
Value="False">
<Setter Property="Height"
Value="Auto" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
<RowDefinition Name="GridRow2"
Height="*" />
</Grid.RowDefinitions>
<Expander x:Name="expanderOne"
Grid.Row="0">
<TextBox TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Expander>
<Expander Grid.Row="1">
<TextBox TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Expander>
</Grid>

WPF populate treeview selection into a textbox

Any help is appreciated. I'm very new to WPF. I have a treeview that is being populated by an xml file. When the treeview item is selected, I need it to show up in a text box. I have the treeview inside a popup and I've got it so that when the textbox in question is selected, it will bring up the popup with treeview prompting the user to make a selection on the treeview. Afterwards it should put that treeview selection back into the same textbox. Here's my code:
<TextBox Name="text"
Text="{Binding Path=SelectedItem.name, ElementName=dirTree}"
Style="{StaticResource CustomTextBoxStyle}"
Grid.Column="1"
Margin="47,326,110,140"
TextChanged="text_TextChanged" />
And the treeview portion:
<Popup PlacementTarget="{Binding ElementName=text}"
VerticalOffset="20"
HorizontalOffset="-180"
Margin="0,0,465,279"
Name="popup1"
AllowsTransparency="True"
Placement="Top">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=text, Path=IsFocused}"
Value="True">
<Setter Property="IsOpen"
Value="True" />
</DataTrigger>
<!--<DataTrigger Binding="{Binding ElementName=popupText, Path=IsFocused}"
Value="True">
<Setter Property="IsOpen"
Value="True" />
</DataTrigger>-->
</Style.Triggers>
</Style>
</Popup.Style>
<Grid>
<Border BorderThickness="2"
Background="DodgerBlue"
BorderBrush="DodgerBlue"
Padding="0"
CornerRadius="6">
<ScrollViewer Height="300"
Name="scrollViewer1"
Width="175"
BorderBrush="Black"
Background="DodgerBlue">
<TreeView Name="dirTree"
ItemsSource="{Binding Source={StaticResource xmldata}, XPath=.}"
VirtualizingStackPanel.IsVirtualizing="False"
VirtualizingStackPanel.VirtualizationMode="Standard"
GotFocus="TreeView1_GotFocus"
SelectedItemChanged="{Binding ElementName=dirTree, Path=SelectedItem}"/>
</ScrollViewer>
</Border>
</Grid>
</Popup>
Create an event handler on the SelectedItemChanged event from your TreeView and from there, update your TextBlock.Text.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TreeView x:Name="demoTreeView"
Margin="10"
SelectedItemChanged="demoTreeView_SelectedItemChanged">
</TreeView>
<TextBlock x:Name="demoTextBox" Grid.Row="1"/>
</Grid>
And in the .cs file :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
demoTreeView.Items.Add("test1");
demoTreeView.Items.Add("test2");
demoTreeView.Items.Add("test3");
demoTreeView.Items.Add("test4");
}
private void demoTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
demoTextBox.Text = e.NewValue.ToString();
}
}
Personalty I would recommend you to use MVVM and bindings on Properties in order to have a nice decoupling between the xaml and the code-behind.

Show "pop up window" when is mouser over listBox item

I bind observable collection on listBox. I have data tempate on listbox item. It consit one image control and som textBlock.
If is mouse over on some listBox item I would like achieve this behavior:
Show PopUp/ToolTip (some "rectangle" with controls) and bind values from listBox current item.
And on textBox in item data template I have style, I would like change color of text in textBlock, for example from black to green.
Style is here:
<Style x:Key="FriedNickStyle" TargetType="TextBlock">
<Setter Property="Margin" Value="2,2,2,2"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="Black"/>
</Style>
Sory for my english, I have problem how describe this behavior correct. I try many thing but any of them doesn’t work good.
Here is it my style:
<DataTemplate x:Key="FriendListBoxItemTemplate">
<Grid Name="RootLayout">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Image Margin="4,4,4,2" Grid.Column="0">
<Image.Source >
<MultiBinding Converter="{StaticResource avatarConverter}">
<Binding Path="ProfilePhoto"></Binding>
<Binding Path="StatusInfo.IsLogged"></Binding>
</MultiBinding>
</Image.Source>
</Image>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock
Text="{Binding Path=Nick}"
Style="{StaticResource FriedNickStyle}"
Grid.Column="0" Grid.Row="0">
</TextBlock>
</Grid>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!--SHOW SOME POP UP WINDOW and bind properties from ITEM (VALUE)-->
<!--Change color of textBlock-->
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
Thank everybody who help me.
Well, I found this turorial, this article, by the MSDN and another stack overflow's question.
Basically, here's how:
<Popup Margin="10,10,0,13"
Name="Popup1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="194"
Height="200"
IsOpen="True"> // change this to open it
<TextBlock Name="McTextBlock" Background="LightBlue" >
This is popup text
</TextBlock>

How do I Show/Hide a Grid Row and Grid Splitter based on a Toggle Button?

Currently I have a toggle button that is bound to a boolean property (DualLayout) in my code behind. When the boolean is set to True, then I want my second row in my grid (and grid splitter) to hide and have the first row take up the entire space of the grid. Once the boolean is set to False, I want the grid splitter and bottom row to appear.
Here is a snippet of my xaml
<ToggleButton Name="toggleLayout" Margin="66,1,0,1" Width="25" HorizontalAlignment="Left" IsChecked="{Binding DualLayout}" Checked="toggleLayout_Clicked" Unchecked="toggleLayout_Clicked">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type ToggleButton}">
<Image Source="Images/PlayHS.png"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Receive and Transmit Windows Split."/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type ToggleButton}">
<Image Source="Images/PauseHS.png"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="Receive and Transmit Windows Combined."/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Grid x:Name="transmissionsGrid" Margin="0,28,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" MinHeight="100" />
</Grid.RowDefinitions>
<transmission:TransmissionsControl x:Name="transmissionsReceive" TransmissionType="Receive" Margin="0,0,0,5" />
<GridSplitter Name="gridSplitter1" Grid.Row="0" Background="White" Cursor="SizeNS" Height="4" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Foreground="Firebrick" />
<transmission:TransmissionsControl x:Name="transmissionsTransmit" TransmissionType="Transmit" Grid.Row="1" />
</Grid>
This is untested, but I believe it should work.
First, if you want your first row to take up the whole space, you'll want to define your RowDefinitions as
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" /> <!-- Edit: Removed MinHeight="100" -->
</Grid.RowDefinitions>
For showing/hiding the controls, you'll need to bind their Visibility property either to your DualLayout property (if the class properly implements INotifyPropertyChanged), or (perhaps more simply) to the IsChecked property of the ToggleButton.
For instance (the same applies to the GridSplitter):
<!-- EDIT: Added MinHeight="100" here instead -->
<transmission:TransmissionsControl x:Name="transmissionsTransmit"
TransmissionType="Transmit"
Grid.Row="1"
MinHeight="100"
Visibility={Binding ElementName=toggleLayout,
Path=IsChecked,
Converter={StaticResource boolToVis}}" />
At some level above the controls in question (here I am doing it at the window level) you need to add built-in BooleanToVisibilityConverter resource:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis" />
</Window.Resources>

Resources