Accessing DataContext properties in View - c

I'm hoping someone can give me a kick in the right direction, I'm currently learning WPF and MVVM - let's say its not been plain sailing. Basically I'm trying to access the properties of DataContext and bind them to a property in my view. I'll be completely honest, I've got myself in a bit of a tangle.
When the user clicks the button in question it fires the code below.
private void OnReceiptClick(object sender, RoutedEventArgs e)
{
var dialogBox = new DisplayReceiptView(((CheckMemberViewModel) this.DataContext).ReceiptViewModel);
dialogBox.ShowDialog();
}
My CheckMemberViewModel currently holds the 'Person' property I'm after, and at this stage DataContext is populated as expected.
The code behind my DisplayReceiptView looks like the following:
public DisplayReceiptView(ReceiptViewModel context) : this()
{
this.DataContext = context;
}
Once again everything seems correct, and finally in my XAML I have
<Grid DataContext="{Binding Path=Person}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="1" Grid.Row="1">Name:</Label>
<TextBox Grid.Column="2" Grid.Row="1" Text="{Binding Path=Person.Forename}"></TextBox>
</Grid>
Unfortunately no matter what I've done, and I think where I'm up to at the moment is the closest I've been, the data doesn't seem to bind. Below is my ViewModel code for the properties
private Person _person;
public Person Person
{
get { return _person; }
set
{
if (value != _person)
{
_person = value;
OnPropertyChanged("Person");
}
}
}
Any help is greatly appreciated.

<TextBox Grid.Column="2" Grid.Row="1" Text="{Binding Path=Person.Forename}"></TextBox>
This is wrong as you are already bound to Person
<TextBox Grid.Column="2" Grid.Row="1" Text="{Binding Forename}"></TextBox>
Is all you need
<TextBox Grid.Column="2" Grid.Row="1" Text="{Binding Forename, Mode=TwoWay}"></TextBox>
Will save changes to the source and retrieve changes, but of course you will need to save context changes to make them permanent

Related

wpf maskedTextBox Databindings

I am using the following MaskedTextBox http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox in a person project for my own use. This is a simple example because i am trying to use this on a much bigger program but I am using this program to test this control.
STEPS:
1. Create a wpf application
2. Added a Linq to SQL Class called Prescriptions and added a table called info
{ID | Name | Phone}
The markup for the form is listed below:
<Window x:Class="MaskedTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="300" Width="300"
Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Label Name="nameLabel" Margin="2" Target="{Binding ElementName=nameTxt}">_Full Name</Label>
<TextBox Name="nameTxt" Grid.Column="1" Margin="2" Text="{Binding Name}"/>
<Label Name="phoneLabel" Margin="2" Grid.Row="1" Grid.Column="0" Target="{Binding ElementName=phoneTxt}">_Phone Numnber</Label>
<xctk:MaskedTextBox Name="phoneTxt" Margin="2" Grid.Row="1" Grid.Column="1" Text="{Binding Phone}" Mask="(000) 000-0000" />
</Grid>
Code Behind File:
public partial class MainWindow : Window
{
private PrescriptionDataContext pdc = new PrescriptionDataContext();
private List<info> users = new List<info>();
private info U = new info { Id = 1, Name = "Matthew Brown", Phone = "5128289081" };
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var q = pdc.getUser();
foreach (info I in q)
{
users.Add(I);
}
// DOES NOT WORK
this.DataContext = users;
// WORKS
// this.DataContext = U;
}
}
When this loads, you will see that the name field has been binded correctly but the phone control shows only the mask not the underlying data from the table. If I explicity create the class and then bind it like i did above it works.
Ideas??

WPF/XAML using TreeViewItems in a TreeView with Databinding and advanced templates

I'm pretty new to XAML and to databinding, so hopefully this will be an easy question to someone out there...
I'm trying to build a TreeView using data binding that has a structure similar to this:
Category
Item Name
Item Name
Category
Item Name
So far, so good--I can do that. Next, I want to be able to expand the Item node and show details about that item. This works fine when I build the tree manually in the code-behind, but I want to use data binding instead. I ALMOST have this working, but as it is now, either I can't select the node OR I can't get the node to behave like a TreeViewItem where it shows just the item name when it is collapsed and can expand to show more--in my case a custom defined grid with data on it.
Here's a simplified sample of my XAML code:
<DataTemplate x:Key="ItemDataTemplate">
<TreeViewItem Header="{Binding Path=ItemName}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Qty on Hand" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Qty}"/>
</Grid>
</TreeViewItem>
</DataTemplate>
<HierarchicalDataTemplate x:Key="CategoryDataTemplate" ItemsSource="{Binding Path=Categories}" ItemTemplate="{StaticResource ItemDataTemplate}">
<TextBlock Text="{Binding Path=CategoryName}"/>
</HierarchicalDataTemplate>
Based on this simplified example, the object that was defined in the code-behind would be something like this:
public class Category
{
public ObservableCollection<Item> Items {get; private set;}
public string CategoryName { get; set; }
//constructor and methods here
}
public class Item
{
public string ItemName {get; private set;}
public decimal Price { get; private set; }
public string Description {get; private set; }
public int Qty {get; set;}
//constructor and methods here
}
After reading this question and answer on stackoverflow, I can see that you cannot have your template be a TreeViewItem if you want to select the node. I tried getting rid of the TreeViewItem tag and going straight to the Grid which allows me to select the node, but now I can't collapse the node down to just the ItemName (header text).
I feel like it's darn close... what am I missing?
So, what you want is to make an Item look like it has more child nodes, even though its only "child" is itself, presented with more info?
Well, the most correct way of doing that might be to use an expander in ItemDataTemplate, like so:
<DataTemplate x:Key="ItemDataTemplate">
<Expander Header="{Binding Path=ItemName}" >
<Grid>
<Grid.ColumnDefinitions>
...
<Grid.RowDefinitions>
...
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock ...
</Grid>
</TreeViewItem>
</DataTemplate>
The expander doesn't look exactly like a standard TreeViewItem, but that can be fixed by making your own Template for the Expander.
Now, a slightly more hacky way of doing it would be to have a list of Items in the Item class, where each Item exposes itself as the only list element. Then, ItemDataTemplate would need to be a HierarchicalDataTemplate which referenced another, more detailed, "ItemDetailsTemlate":
public class Item
{
...
public List<Item> InfoWrapper
{
get { return new List<Item>() { this }; }
}
}
Xaml:
<DataTemplate x:Key="ItemDetailsTemplate">
<Grid>
<Grid.ColumnDefinitions>
...
<Grid.RowDefinitions>
...
<TextBlock Grid.Row="0" Grid.Column="0" Text="Price" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Price}"/>
<TextBlock ...
</Grid>
</DataTemplate>
<HierarchicalDataTemplate x:Key="ItemDataTemplate" ItemsSource="{Binding Path=InfoWrapper}" ItemTemplate="{StaticResource ItemDetailsTemplate}" >
<TextBlock Text="{Binding Path=ItemName}"/>
</HierarchicalDataTemplate>

object sender is always null in RelayCommand

I am using RelayCommand to handle a button click, I need to get the sender parameter but it is always null, any idea why?
ViewModel.cs
private RelayCommand _expandClickCommand;
public ICommand ExpandClickCommand
{
get
{
if (_expandClickCommand == null)
{
_expandClickCommand = new RelayCommand(ExpandClickCommandExecute, ExpandClickCommandCanExecute);
}
return _expandClickCommand;
}
}
public void ExpandClickCommandExecute(object sender)
{
//sender is always null when i get here!
}
public bool ExpandClickCommandCanExecute(object sender)
{
return true;
}
View.xaml
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Column="0" Grid.Row="0" Content="Expand" Command="{Binding DataContext.ExpandClickCommand,ElementName=SprintBacklog}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I need to get the index of the currect ListboxItem in ExpandClickCommand
That object in all likelihood is not the sender but the CommandParameter that is passed by the control. You could bind the CommandParameter of the button to itself to immitate the sender.
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
(But that might not really help you that much, so think about what you pass in that helps you get that value.)

Binding Items inside a DataTemplate with Items from another DataTemplate

I have two Data Template (one for drawing[draw] and another for Input Data[data]) Also I have the two ContentControls which uses the above DataTemplates.
I want the both DataTemplate's elements to be binded so that when the user fills in a field in the data form DateTemplate it automatically updates the draw Template as well.
How can I bind the elements in draw DataTemplate with the elements of data DataTemplate.
There is no backend data at all. User picks up a value from a combobox and based upon the value selected in combobox I update the two ContentControls with relevant draw and data DataTemplates. User fill in the relevant fields in the data form and draw template draws those elements based upon some business Rules.
-----
<DataTemplate x:Key="data">
<Grid Grid.Row="0" Background="#FFFFFFFF" Name="DocumentRoot" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Margin="10" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Text="Heading Text" Grid.Row="1"/>
<TextBlock Text="Ticket Text" Grid.Row="2"/>
-----
<TextBox x:Name="txtHeading" Text="Heading Text" Grid.Row="1" Grid.Column="1"/>
<TextBox x:Name="txtTicketText" Text="Ticket Text" Grid.Row="2" Grid.Column="1"/>
-----
</Grid>
</Grid>
</DataTemplate>
<ContentControl Content="{Binding ElementName=cboTemplates, Path=SelectedItem.Name}"
ContentTemplateSelector="{StaticResource formTemplateSelector}">
</ContentControl>
Any ideas how can I bind the two elements from inside different DataTemplates?
Thanks in advance
Consider creating class (named View Model) and bind both templates to single instance of that class (this is Model-View-ViewModel design pattern). Otherwise you probably will have very complex bindings contains hardcoded logical tree.
Why don't you bind one object (of class with a Draw property and a Data property) to both the templates. When one template changes Data property in the object, you can refresh Draw property in the object which in turn will update the Draw template.
Updated
Example :
Window Content
<Grid>
<StackPanel>
<ContentControl DataContext="{Binding}">
<ContentControl.Template>
<ControlTemplate>
<Rectangle Fill="{Binding Background}"
Width="200"
Height="200" />
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
<ContentControl DataContext="{Binding}">
<ContentControl.Template>
<ControlTemplate>
<TextBox Text="{Binding ColorText}" />
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</StackPanel>
</Grid>
Code Behind
public partial class MultiViewWindow : Window
{
public MultiViewWindow()
{
InitializeComponent();
DataContext = new BackgroundInfo();
}
}
public class BackgroundInfo : INotifyPropertyChanged
{
protected String _colorText;
public String ColorText
{
get
{
return _colorText;
}
set
{
_colorText = value;
RaisePropertyChanged("ColorText");
RaisePropertyChanged("Background");
}
}
public Brush Background
{
get
{
try
{
return new SolidColorBrush((Color)ColorConverter.ConvertFromString(ColorText));
}
catch (Exception)
{
return new SolidColorBrush(Colors.Transparent);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(String propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Binding Silverlight UserControl custom properties to its' elements

I'm trying to make a simple crossword puzzle game in Silverlight 2.0. I'm working on a UserControl-ish component that represents a square in the puzzle. I'm having trouble with binding up my UserControl's properties with its' elements. I've finally (sort of) got it working (may be helpful to some - it took me a few long hours), but wanted to make it more 'elegant'.
I've imagined it should have a compartment for the content and a label (in the upper right corner) that optionally contains its' number. The content control probably be a TextBox, while label control could be a TextBlock. So I created a UserControl with this basic structure (the values are hardcoded at this stage):
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="7"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="A"
BorderThickness="0" />
</Grid>
</UserControl>
I've also created DependencyProperties in the Square class like this:
public static readonly DependencyProperty LabelTextProperty;
public static readonly DependencyProperty ContentCharacterProperty;
// ...(static constructor with property registration, .NET properties
// omitted for brevity)...
Now I'd like to figure out how to bind the Label and Content element to the two properties. I do it like this (in the code-behind file):
Label.SetBinding( TextBlock.TextProperty, new Binding { Source = this, Path = new PropertyPath( "LabelText" ), Mode = BindingMode.OneWay } );
Content.SetBinding( TextBox.TextProperty, new Binding { Source = this, Path = new PropertyPath( "ContentCharacter" ), Mode = BindingMode.TwoWay } );
That would be more elegant done in XAML. Does anyone know how that's done?
First, set the DataContext on the UserControl using {RelativeSource Self}:
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Now you can bind the individual elements to the properties of the usercontrol:
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="{Binding LabelText}"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="{Binding ContentCharacter}" BorderThickness="0" />
For SL 2.0, you'll need to set the DataContext on the UserControl's Loaded event handler.
private void UserControl_Loaded( object sender, RoutedEventArgs e ) {
LayoutRoot.DataContext = this;
}
As Silverlight cannot use FindAncestor technique you can use a trick similar to the one that sets the UserControl's name, but without breaking its functionality by using the name of the LayoutRoot...
<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30"
Width="100" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="{Binding Path=Parent.LabelText, ElementName=LayoutRoot}" Grid.Row="0" Grid.Column="1"
Text="7"/>
<TextBox x:Name="{Binding Path=Parent.ContentCharacter, ElementName=LayoutRoot}" Grid.Row="1" Grid.Column="0"
Text="A"
BorderThickness="0" />
</Grid>
</UserControl>
It worked in SL3 without having to add any additional code (I'm using it in a WP7 app), but don't know if you can use it in SL2. Well, I realize now how this question is old, hope it's still helpful, I've arrived here because the answers I got for the same problem in WP7 didn't convince me.
I think you are looking for UI Element to Element Binding which is a feature of Silverlight 3.
I may not be understanding your issue exactly. In Silverlight, you are able to bind to almost any data object. So, if you have a PuzzleSquare class that contains properties Content and Label, you may bind to these properties directly from the object.
Let's say you created a simple object PuzzleSquare:
public class PuzzleSquare
{
public string Content{ get; set; }
public string Label{ get; set; }
public void PuzzleSquare(){};
public void PuzzleSquare(string label, string content):this()
{
Content = content;
Label = label;
}
}
So, if you are building the app with the classic view/code behind model, your code behind would add this object to the DataContext property of the grid on page load:
LayoutRoot.DataContext = new PuzzleSquare("1", "A");
Your Xaml would bind to the Square property:
<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1"
Text="{Binding Label}"/>
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"
Text="{Binding Content}" BorderThickness="0" />
Does that make sense?
ib.
This worked in Silverlight 4.0
Put a name on the UserControl, and then refer to it in the TextBlock
<UserControl x:Class="XWord.Square"
...omitted for brevity ...
x:Name="Square">
<TextBlock x:Name="Label" ...
Text="{Binding Path=LabelText,ElementName=Square}"/>
Try this:
Public ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(ButtonEdit), New System.Windows.PropertyMetadata("", AddressOf TextPropertyChanged))
Public Property Text As String
Get
Return GetValue(TextProperty)
End Get
Set(ByVal value As String)
SetValue(TextProperty, value)
End Set
End Property
Private Sub TextPropertyChanged()
If String.IsNullOrEmpty(Text) Then
TextBox1.Text = ""
Else
TextBox1.Text = Text
End If
End Sub
Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles TextBox1.LostFocus
Text = TextBox1.Text
End Sub
I can bind in both XAML and code behind.

Resources