WPF : MVVM Microsoft.Expression.Interaction - wpf

I am working on MVVM , I have used interactions.
Purpose is what ever will be typed in text box, same will be displayed in TextBlock.
Interaction trigger will be done event PreviewTextInput of text box.
But it is giving error but not working as expected.
Binding error at runtime image has been attached.
Below is the code.
XAML
<Window x:Class="MVVMApp.TextBindings"
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:MVVMApp"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="Text Bindings" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
<RowDefinition Height="100"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Enter Text Value" Grid.Row="0" Grid.Column="0"/>
<TextBox Name="txtBox1" Text="{Binding BxVal,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1" Width="150" Height="30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewTextInput">
<i:InvokeCommandAction Command="{Binding cmdType}" CommandParameter="{Binding BxVal,ElementName=txtBox1}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBlock Text="Result --> " Grid.Row="1" Grid.Column="0"/>
<TextBlock Text="{Binding Result,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="Lavender" Grid.Row="1" Grid.Column="1"/>
</Grid>
MVVM
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVVMApp.Helper;
namespace MVVMApp.ViewModel
{
class TextBindingsVM :ViewModelBase
{
DelegateCommand <object> cmdType { get; set; }
private string _Result { get; set; }
public string Result
{
get { return _Result; }
set
{
_Result = value;
OnPropertyChanged(this, "Result");
}
}
private string _BxVal { get; set; }
public string BxVal
{
get { return _BxVal; }
set
{
_BxVal = value;
OnPropertyChanged(this, "BxVal");
Result = BxVal;
}
}
public TextBindingsVM()
{
cmdType = new DelegateCommand<object>(cmdType_Execute);
}
private void cmdType_Execute(object obj)
{
throw new NotImplementedException();
}
}
}

I found the issue. Actually INotifyPropertyChanged was not implemented in ViewModelBase class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMApp.Helper
{
class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(object sender, string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
}

Related

Fill textbox with a property with Binding MVVM

I try to figure out how MVVM works and want to start simple.
I made a Model, a View and a ViewModel. I hooked up the view to a window. this works more or less.
in the view i have a textbox which i want to fill with the value of a property. The textbox keeps empty!?
This is what i have:
Model:
namespace Qbox_0001.Model
{
public class RegistratieModel
{
}
public class Registratie : INotifyPropertyChanged
{
//Fields
private string programmaNaam;
//eventhandler die kijkt of een property wijzigt
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
//Properies
public string ProgrammaNaam
{
get { return programmaNaam; }
set
{
if (programmaNaam != value)
{
programmaNaam = value;
RaisePropertyChanged("ProgrammaNaam");
}
}
}
}
}
The View:
<UserControl x:Name="UserControlRegistratie" x:Class="Qbox_0001.Views.RegistratieView"
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:Qbox_0001.Views"
mc:Ignorable="d" Height="1000" Width="860" MaxWidth="860" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">
<Grid x:Name="GridRegistratie">
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDef_Menu" Height="21*" MaxHeight="21" MinHeight="21"/>
<RowDefinition x:Name="RowDef_TabControl" Height="500*" MinHeight="500"/>
<RowDefinition x:Name="Rowdef_Bottom" Height="40*" MaxHeight="40" MinHeight="40"/>
</Grid.RowDefinitions>
<Grid x:Name="Grid_Registratie_WorkArea" Grid.Row="1">
<TabControl x:Name="TabControl_Registratie">
<TabItem Header="Registratie">
<Grid x:Name="Grid_Tab_Registratie">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="GridRowDef_Algemeen" MinHeight="68" Height="68*" MaxHeight="68"/>
<RowDefinition x:Name="GridRowDef_Locatie" MinHeight="120" Height="120*" MaxHeight="120"/>
<RowDefinition x:Name="GridRowDef_AantalDagen" MinHeight="105" Height="105*" MaxHeight="105"/>
<RowDefinition x:Name="GridRowDef_MaxDagen" MinHeight="105" Height="105*" MaxHeight="105"/>
<RowDefinition x:Name="GridRowDef_Lokaal" MinHeight="100" Height="100*" MaxHeight="100"/>
<RowDefinition x:Name="GridRowDef_LicBestand" Height="150*" MaxHeight="150" MinHeight="150"/>
</Grid.RowDefinitions>
<GroupBox x:Name="GroupBox_algemeen" Header="Algemeen" Margin="10,4,10,3">
<Grid>
<Label x:Name="Label_Klant" Content="Klant:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Padding="0,5,5,5"/>
<Label x:Name="Label_Programma" Content="Programma:" HorizontalAlignment="Left" Margin="356,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="Label_Versie" Content="Versie:" HorizontalAlignment="Left" Margin="645,10,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="textBox_KlantNaam" HorizontalAlignment="Left" Height="23" Margin="49,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="288"/>
<!-- the textbox keeps empty -->
<TextBox x:Name="TextBox_ProgrammaNaam" Text="{Binding ElementName=RegistratieViewModel, Path=ProgrammaNaam, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="431,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="190" IsEnabled="False" />
<TextBox x:Name="TextBox_Versie" HorizontalAlignment="Left" Height="23" Margin="695,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" IsEnabled="False" />
</Grid>
</GroupBox>
</Grid>
</Border>
</Grid>
</TabItem>
<TabItem Header="Licentie(s)">
<Grid x:Name="Grid_Tab_Licentie" Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Grid>
</Grid>
</UserControl>
In the View.cs:
namespace Qbox_0001.Views
{
/// <summary>
/// Interaction logic for RegistratieView.xaml
/// </summary>
public partial class RegistratieView : UserControl
{
public RegistratieView()
{
InitializeComponent();
this.DataContext = new Qbox_0001.ViewModel.RegistratieViewModel();
}
}
}
The ViewModel
using Qbox_0001.Model; //
using System.Collections.ObjectModel; //
namespace Qbox_0001.ViewModel
{
public class RegistratieViewModel
{
public RegistratieViewModel()
{
loadRegistratieOnderdelen();
}
public ObservableCollection<Registratie> RegistratieOnderdelen //Registratie = "public class Registratie : INotifyPropertyChanged" van de Model
{
get;
set;
}
public void loadRegistratieOnderdelen()
{
ObservableCollection<Registratie> regOnderdelen = new ObservableCollection<Registratie>();
regOnderdelen.Add(new Registratie { ProgrammaNaam = "Test" });
}
}
}
I can see a couple of problems with your code.
You are populating a local ObservableCollection (inside your loadRegistratieOnderdelen() method) with data but since its local, it is not a member of the DataContext and hence unavailable to the View. You have to use public properties like RegistratieOnderdelen which you already declared in your RegistratieViewModel.
Next you are using an ObservableCollection whereas you might just want to use a property of type String. Collections are used when you want to represent lists, for example inside a ListView or an ItemsControl. Your view indicates that you want to bind a single text value so a public property of type String makes more sense.
Best, Nico
The DataContext is a RegistratieViewModel. This class has a RegistratieOnderdelen property that returns a collection of Registratie objects.
You could bind to the ProgrammaNaam property of one such item but you need to specify which item to bind to, for example the first one:
<TextBox x:Name="TextBox_ProgrammaNaam" Text="{Binding Path=RegistratieOnderdelen[0].ProgrammaNaam, UpdateSourceTrigger=PropertyChanged}" ... />

How to add controls inside WPF TabControl via XAML

I have the following XAML, a TabControl which binds to an ObservableCollection and creates my tabs just fine.
<Window x:Class="BA_Auditing.AuditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BizeAsset - Audit Results" Height="700" Width="1120" WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid>
<TabControl Name="ModuleTabControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" ItemsSource="{Binding}" >
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding DISPLAY_NAME}"/>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Search:" HorizontalAlignment="Right"/>
<TextBox x:Name="tbxSearch" Grid.Column="1"/>
</Grid>
<TextBlock Grid.Row="2" Text="Items Selected: 0 of 908" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Next I'd like to populate each tab area with the next level of controls, which will include a Label, TextBox another TabControl and a TextBlock.
I previously wrote this in WinForms and this is what it looks like:
What XAML do I need add to do this?
That is because I am designing it dynamically via binding rather than literally adding a TabItem
[EDIT]
I have tried to enter controls into the TabControl.ContentTemplate however nothing displays in the body of the TabItem.
I think if you had "clicked" on the "WW - Wastewater" tab you would have seen something being generated (the Search box, etc) - that's because the tab wasn't selected by default.
Anyway, here is a bit of code which gets you a bit closer to what you want - it's just to get you started, you'll need to add the other plumbing code (change notification, etc).
I don't know what you intend to have in the "Services" tab, etc...so don't know if you can handle them all in the same way i.e. as "Assets". Also you might want to explicitly define the names of the grid columns rather than have them auto-generated - there are some techniques elsewhere you can find to do that.
<Window x:Class="WpfApp38.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:local="clr-namespace:WpfApp38"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TabControl Name="ModuleTabControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" ItemsSource="{Binding}" SelectedIndex="0" >
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding DISPLAY_NAME}"/>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Search:" HorizontalAlignment="Right"/>
<TextBox x:Name="tbxSearch" Grid.Column="1"/>
</Grid>
<TabControl Grid.Row="1" ItemsSource="{Binding SubCategories}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DISPLAY_NAME}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<ItemContainerTemplate>
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Assets}">
</DataGrid>
</ItemContainerTemplate>
</TabControl.ContentTemplate>
</TabControl>
<TextBlock Grid.Row="2" Text="Items Selected: 0 of 908" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp38
{
public class InfrastructureCateogry
{
public string DISPLAY_NAME { get; set; }
public ObservableCollection<AssetCategory> SubCategories { get; set; }
}
public class AssetCategory
{
public string DISPLAY_NAME { get; set; }
public ObservableCollection<AssetRecord> Assets { get; set; }
}
public class AssetRecord
{
public string AssetID { get; set; } // make it an int
public string AssetType { get; set; }
public string LastUpdateBy { get; set; } // make this a DateTime object
public string LastUpdateDate { get; set; } // make this a DateTime object
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<InfrastructureCateogry> infrastructurecategories = new ObservableCollection<InfrastructureCateogry>();
public MainWindow()
{
InitializeComponent();
var x = new InfrastructureCateogry()
{
DISPLAY_NAME = "AR - Roads and Bridges",
SubCategories = new ObservableCollection<AssetCategory>
{
new AssetCategory
{
DISPLAY_NAME = "Lines",
Assets = new ObservableCollection<AssetRecord>
{
new AssetRecord
{
AssetID = "20040927104600",
AssetType = "Gravity Main",
LastUpdateDate = "07/05/2015 17:01:55 PM"
},
new AssetRecord
{
AssetID = "20150507170116",
AssetType = "Relined",
LastUpdateDate = "07/05/2015 17:01:15 PM"
}
}
},
new AssetCategory
{
DISPLAY_NAME = "Points"
},
new AssetCategory
{
DISPLAY_NAME = "Plant/Components"
},
new AssetCategory
{
DISPLAY_NAME = "Services"
}
}
};
infrastructurecategories.Add(x);
var x2 = new InfrastructureCateogry();
x2.DISPLAY_NAME = "WW - WasteWater";
infrastructurecategories.Add(x2);
this.DataContext = infrastructurecategories;
}
}
}

How I can print listbox item count along with Expander header?

I want to print listbox item count alog with header of expander like
header1 (count) . How can I achieve this in WPF
You haven't provided any code for what you have done, such as if you are using a ViewModel or doing code-behind. There are multiple ways to go about it.
My ViewModel Way
In this example, I've made a ViewModel containing an ObservableCollection of string to populate the ListBox. The Expander header is bound to a property that is populated using a combination of the HeaderText and ItemCount.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _headerText = string.Empty;
private string _headerTextFull = string.Empty;
private ObservableCollection<string> _listItems = new ObservableCollection<string>();
private int _itemCount = 0;
public ViewModel() { }
public string HeaderText
{
get { return _headerText; }
set
{
_headerText = value;
NotifyPropertyChanged("HeaderText");
UpdateHeader();
}
}
public string HeaderTextFull
{
get { return _headerTextFull; }
set
{
_headerTextFull = value;
NotifyPropertyChanged("HeaderTextFull");
}
}
public ObservableCollection<string> ListItems
{
get { return _listItems; }
set
{
_listItems = value;
NotifyPropertyChanged("ListItems");
ItemCount = (_listItems != null ? _listItems.Count : 0);
}
}
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = value;
NotifyPropertyChanged("ItemCount");
UpdateHeader();
}
}
private void UpdateHeader()
{
HeaderTextFull = String.Format("{0} ({1})", _headerText, _itemCount);
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The XAML for the Window:
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander Grid.Row="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
The code-behind:
public partial class Window1 : Window
{
ViewModel myModel = new ViewModel();
public Window1()
{
InitializeComponent();
myModel.ListItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ListItems_CollectionChanged);
myModel.HeaderText = "Items";
myModel.ListItems.Add("Item 1");
myModel.ListItems.Add("Item 2");
this.DataContext = myModel;
}
void ListItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
myModel.ItemCount = myModel.ListItems.Count;
}
void Button_Click(object sender, RoutedEventArgs e)
{
myModel.ListItems.Add("Another item");
}
}
When it starts, the Expander header will say "Items (2)". Every time the button is clicked, the header will update to show the new count.
Slight Variation of Above
Here's an example that provides the above example, but also adds a second list to demonstrate a different way. Notice the Expander.Header section.
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Expander Grid.Row="0" Grid.Column="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<!-- SECOND EXPANDER THAT DOESN'T RELY ON A VIEWMODEL -->
<Expander Grid.Row="0" Grid.Column="1" Name="expander2" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ElementName=listBox2, Path=Items.Count, UpdateSourceTrigger=PropertyChanged, StringFormat={}Items ({0})}" />
</StackPanel>
</Expander.Header>
<Grid>
<ListBox Name="listBox2" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Grid.ColumnSpan="2" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
Only Code Behind
If, for some reason, you are just using code-behind, you can do it this way:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
((INotifyCollectionChanged)listBox1.Items).CollectionChanged += new NotifyCollectionChangedEventHandler(ListBox_CollectionChanged);
}
void ListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
expander1.Header = "Items (" + listBox1.Items.Count + ")";
}
}

Wpf TabControl Interaction.Trigger fires mores than once

I have an unexpected behaviour with a Wpf-Tabcontrol and Dataemplate with Interaction.Trgiggers.
First I define a "ViewModel". It's quite simple:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication3 {
public class Vm: INotifyPropertyChanged {
public Vm() {
this.CmdClick = new RelayCommand( p => this.ExecuteClick(), p => this.CanExecuteClick() );
}
private string myName;
public string MyName {
get { return myName; }
set {
if( myName != value ) {
myName = value;
if( this.PropertyChanged != null ) {
this.PropertyChanged( this, new PropertyChangedEventArgs( "MyName" ) );
}
}
}
}
private RelayCommand cmdClick;
public RelayCommand CmdClick {
get { return cmdClick; }
set { cmdClick = value; }
}
#region Command
private bool CanExecuteClick() {
return true;
}
public void ExecuteClick() {
MessageBox.Show( "MyName is " + MyName );
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
}
}
Next, I define a Datatemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:l="clr-namespace:WpfApplication3"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate DataType="{x:Type l:Vm}" x:Key="Vm">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Text="{Binding Path=MyName}" Grid.Row="0"/>
<i:Interaction.Triggers>
<ei:KeyTrigger Key="F5" ActiveOnFocus="True" FiredOn="KeyUp" >
<i:InvokeCommandAction Command="{Binding CmdClick}" />
</ei:KeyTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>
</ResourceDictionary>
I use the datatemplate on the mainform inside a TabControl:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication3"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<TabControl>
<TabItem Header="Tab1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentControl ContentTemplate="{StaticResource Vm}" x:Name="vm11" Content="{Binding}" Grid.Row="0"/>
</Grid>
</TabItem>
<TabItem Header="Tab2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl ContentTemplate="{StaticResource Vm}" x:Name="vm21" Content="{Binding}" Grid.Row="0"/>
<ContentControl ContentTemplate="{StaticResource Vm}" x:Name="vm22" Content="{Binding}" Grid.Row="1"/>
</Grid>
</TabItem>
</TabControl>
</Window>
The code behind is simple:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication3 {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
this.vm11.Content = vm1;
this.vm21.Content = vm2;
this.vm22.Content = vm3;
this.vm1.MyName = "Name1";
this.vm2.MyName = "Name2";
this.vm3.MyName = "Name3";
}
Vm vm1 = new Vm();
Vm vm2 = new Vm();
Vm vm3 = new Vm();
}
}
When i run the program, I chlick in the first TextBox, press F5 and the MessageBox posup. Ok.
I switch the tabpage, click inside the second TextBox, press F5 and the MessageBox appears again. OK.
I switch back to the firsz tabpage, press F5 and the MessageBox appears twice. Uuuh. Whatss wrong.
I switch back to the second tabpage,the messagebox appears twice again.
After switching to the first tabpage, the messagebox is displayed treetimes now. And so on.
It is like, the KeyUp-Event is assigned internaly, when the datatemplate is displayed again, without dereferencing it. But ehat can I do ?
I think you need to change this property from
private RelayCommand cmdClick;
public RelayCommand CmdClick {
get { return cmdClick; }
set { cmdClick = value; }
To
private RelayCommand cmdClick;
public RelayCommand CmdClick {
get { return cmdClick; }
set { if(cmdClick == null)cmdClick = value; }

Scrollable form control in Windows Phone 8

How do I make a scrollable form similar to that of the Phone Contact input panel in Windows Phone 8.1?
The current method of StackPanel inside a ScrollViewer restricts scrolling when the SIP launches and I have to hit back button to choose other TextBoxes.
This is not an idea UX situation and I've tried a few options of the web.
Increasing the StackPanel's height beyond its necessary size by about
350 pixels - didn't work, as it displaces the form unevenly and
doesn't return to normal
Creating a ListBox as suggested in another site online didn't change anything either
Increasing the down margin of the last control didn't help either
check the below code which has worked fine for me.
Chat.xaml.cs
using System;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using Microsoft.Phone.Controls;
using PhoneApplicationPage = Microsoft.Phone.Controls.PhoneApplicationPage;
namespace LIV.View
{
// ReSharper disable once RedundantExtendsListEntry
public partial class Chat : PhoneApplicationPage
{
private bool _flag;
public Chat()
{
InitializeComponent();
}
#region Static Chat Header WorkAround
public double OldHeight;
private TranslateTransform _translateTransform;
#region TranslateY dependency property
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register(
"TranslateYProperty", typeof(double), typeof(Chat), new PropertyMetadata(default(double), PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var chat = o as Chat;
if (chat != null)
{
chat.UpdateTopMargin((double)e.NewValue);
}
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
#endregion
private void ChatPage_OnLoaded(object sender, RoutedEventArgs e)
{
var transform = ((Application.Current).RootVisual).RenderTransform as TransformGroup;
if (transform != null)
{
_translateTransform = transform.Children.OfType<TranslateTransform>().FirstOrDefault();
if (_translateTransform != null)
{
var binding = new Binding("Y")
{
Source = _translateTransform
};
BindingOperations.SetBinding(this, TranslateYProperty, binding);
}
}
}
private void UpdateTopMargin(double translateY)
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
#endregion
}
}
Chat.xaml
<phone:PhoneApplicationPage x:Class="LIV.View.Chat"
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:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="Black"
Loaded="ChatPage_OnLoaded"
Orientation="Portrait"
SupportedOrientations="Portrait"
shell:SystemTray.BackgroundColor="#FF5CBFBB"
shell:SystemTray.ForegroundColor="White"
shell:SystemTray.IsVisible="True"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Background="#FF5CBFBB">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Foreground="White" Text="{Binding ChatUser.Name}" />
<TextBlock Foreground="White" Text="{Binding ChatTime}" />
</StackPanel>
<Button Grid.Column="1"
BorderThickness="0"
Command="{Binding TerminateCommand}"
Foreground="White">
End Chat
</Button>
</Grid>
<Grid x:Name="ContentPanel" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="MessageListBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding Messages}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Style="{StaticResource BottomListBoxStyle}">
</ListBox>
<StackPanel x:Name="MessageTextPanel"
Grid.Row="1"
Background="#FF5CBFBB">
<Grid Margin="0,0,0,-10">
<TextBlock FontSize="15" Text="{Binding UserStatus}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox></TextBox>
</Grid>
</StackPanel>
</Grid>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
check: ScrollViewer not scroll up while Keyboard is active
When normal
When SIP open
How about only extending the bottom margin of the StackPanel when the SIP is shown? IIRC there is no straightforward API to test for that, but you could use the focus state of the TextBoxes to check when it appears or disappears.

Resources