WPF/CaliburnMicro Bind one property to two form controls - wpf

I have one PersonModel object, which has two properties, FirstName and LastName. I created a CurrentPerson property of type PersonModel in my ViewModel. When binding to the controls, whichever is bound to x:Name is the only one that shows up at runtime
TextBlock Displays FirstName
<TextBlock x:Name="CurrentPerson_FirstName" Grid.Column="0" Grid.Row="0"></TextBlock>
<TextBox Text="{Binding Path=CurrentPerson_FirstName}" Grid.Column="0" Grid.Row="1"/>
TextBox Displays FirstName
<TextBlock Text="{Binding Path=CurrentPerson_FirstName}" Grid.Column="0" Grid.Row="0" ></TextBlock>
<TextBox Name="CurrentPerson_FirstName" Grid.Column="0" Grid.Row="1" />
How can I have both the TextBlock and the TextBox display the same data, and if the TextBox is typed in, the TextBlock and CurrentPerson will be updated?

When not using x:Name, you should stick to the conventional naming pattern of using ".". In your TextBlock, you need to replace "CurrentPerson_FirstName" with "CurrentPerson.FirstName".
For example,
<TextBlock Text="{Binding Path=CurrentPerson.FirstName}" Grid.Column="0" Grid.Row="0" ></TextBlock>
This should help you bind both control to same property.

Related

XAML Itemscontrol binding to property outside of the control

I have a list of transactions that I am loading into an ItemsControl but I want to show the UserId from my viewmodel in each row/transaction. The userId is not in the transaction list, it's a property in the viewmodel I am trying to access but it seems everything I've tried doesn't work and I don't know why. All I get is a blank column value. (yes, I did confirm the property is not empty. even hardcoded a value and got nothing).
My understanding is I should be able to use the RelativeSource with an AncestorType of Window to access the property. Am I missing something?
<ItemsControl ItemsSource="{Binding Path=MyReportResult.Transactions}" KeyboardNavigation.IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="20 2 0 2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".15*"/>
<ColumnDefinition Width=".15*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" TextAlignment="Center" Margin="10 0 20 0" Text="{Binding Path=UserId, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"></TextBlock>
<TextBlock Grid.Column="0" TextAlignment="Center" Margin="10 0 20 0" Text="{Binding Path=Amount}"></TextBlock>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Your RelativeSource binding tries to find the property in the parent Window, although it is in the Window's DataContext.
The following should work, provided that the UserId property is in the same view model class as MyReportResult:
<TextBlock Text="{Binding Path=DataContext.UserId,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
Only WPF supports RelativeSource because it's not strictly necessary. It's less fragile to use ElementName instead like so:
First, give an x:Name="something" to your top level object or its child (usually a Grid).
Text="{Binding Path=DataContext.UserId, Element=something}"

Getting current Object in Control template

<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel>
<StackPanel Margin="0,0,28,0" Orientation="Horizontal" Visibility="{Binding IsEditable,Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock Foreground="Gray" Text="{Binding DateCreated,Converter={StaticResource DateTimeConverter}}" FontFamily="/Assets/Fonts/Berthold Akzidenz Grotesk BE Regular.ttf" FontSize="16"/>
<TextBlock Text=":" Foreground="Gray"/>
<TextBlock Width="20"/>
<TextBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0" Name="TrainerNoteText" Text="{Binding TrainerNote}" FontFamily="/Assets/Fonts/Berthold Akzidenz Grotesk BE Regular.ttf" Foreground="Black" FontSize="16" TextWrapping="Wrap" KeyUp="EditTrainerNote" Width="400"/>
</StackPanel>
</StackPanel>
</ControlTemplate>
The above control template is in a listview. The textbox inside is editable. So when user presses the enter key, I need to get the current object associated with that. How to do this?
You can listen to KeyDown RoutedEvent at ListView level.
http://msdn.microsoft.com/en-us/library/system.windows.input.keyboard.keydown.aspx
Its an attached event and its handler can be placed anywhere in VisualTree.
Here is an example:
<StackPanel TextBox.KeyDown="OnKeyDownHandler">
<TextBox Width="300" Height="20"/>
</StackPanel>
And this is the handler:
public void OnKeyDownHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
TextBox tbx = (TextBox)sender;
tbx.....
}
}
You know, you really should define what your items look like in a DataTemplate defined in the ListBox.ItemTemplate property and not the ListBoxItem.Template property. Based on the example from the linked page:
<ListBox Width="400" Margin="10" ItemsSource="{Binding YourCollectionProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When Binding a collection property to the ListBox.Items property, all of the UI elements inside the DataTemplate will have access to the properties of the type that is in the collection. In this example, the type that populates the YourCollectionProperty collection has TaskName, Description and Priority properties in it. You can replace these properties with those from the type that is in your collection property.
If you set up your properties to implement the INotifyPropertyChanged interface (or use DependencyProperties then any updates in the UI elements will automatically be updated in the data objects in the view model/code behind. Therefore, there is no need to add KeyDown or KeyUp handlers. For more information, please read the Data Binding Overview page on MSDN.

WPF - Filtering a DataCollection with an autocompletebox

I have a view and ViewModel that are working perfectly. I have recently added an AutocompleteBox (found in the WPF Toolkit) which allows users to quickly look up an item.
My view is as such:
An ItemsControl containing my CollectionViewSource named People. Generating perfectly
An AutocompleteBox where the dropdown shows only the items containing the values the user is typing in the AutocompleteBox. Works well. If I type John, all of the people in my CollectionViewSource named People with the word John in the name appear in the dropdown.
My issue is: how do I filter my ItemsControl when the user selects the item he wishes to see from the Dropdown?
My code so far in XAML to bind the data:
<toolkit:AutoCompleteBox Height="25" Width="400"
Foreground="AliceBlue"
ItemsSource="{Binding People.View}"
ValueMemberPath="Name"
Text="{Binding Name}"
IsTextCompletionEnabled="True"
FilterMode="Contains"
Background="#303030">
<toolkit:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<Grid Width="360" HorizontalAlignment="Left">
<StackPanel Orientation="Vertical" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="300">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" Foreground="#25A0DA"
FontSize="14" Width="300"/>
<TextBlock Text="{Binding Status}" FontWeight="Normal" Foreground="White"
FontSize="10" Width="300"/>
</StackPanel>
</Grid>
</DataTemplate>
</toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>
<ItemsControl x:Name="tStack" Grid.Column="0" Grid.Row="1"
ItemsSource="{Binding People.View}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
BorderThickness="0.5">
</ItemsControl>
The itemsControl is styled and the format of the items inside it are also templated/styled but far too long and unconstructive to post here.
From the Name property setter in your viewmodel to which toolkit:AutoCompleteBox.Text is bound, you will have to filter the ObservableCollection backing your Collectionview i,e ItemsSource of your ItemsControl.
If you have your Collectionsource with you then you can have filter applied to it like below:
ICollectionView _peopleView = CollectionViewSource.GetDefaultView(peoples);
_peopleView .Filter = PeopleFilter
private bool PeopleFilter(object item)
{
People people= item as People;
return people.Name.Contains( _filterString ); //Here filter string will be your Name prperty value
}
From Setter of name property you will hav to call _peopleView .Refresh(); to apply the filter
Thanks

Why is binding result different on a Label and a TextBox?

I was using the following XAML:
<Label Grid.Row="0" Grid.Column="0" Content="Datum"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding TimeStamp, StringFormat={}{0:yyyy-MM-dd HH:mm:ss.fff}}"/>
<Label Grid.Row="0" Grid.Column="2" Content="Level"/>
<Label Grid.Row="0" Grid.Column="3" Content="{Binding Level}"/>
but the TimeStamp was being formatted like this:
2.24.2012 7:38
I started up Snoop (great tool!) and noticed that the Label is actually composed of a TextBox and that this TextBox contained the TimeStamp formatted as I defined it. I then replaced the Label with a TextBox and I get the TimeStamp correctly formatted.
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TimeStamp, StringFormat={}{0:yyyy-MM-dd HH:mm:ss.fff}}"/>
2012-02-24 07:38:23.123
I have defined no Resource, Trigger or Style blocks to override Label behaviour so I'm wondering why this is happening.
Any ideas?
The Binding.StringFormat property doesn't work on Labels, you need to use the ContentStringFormat property on the Label
<Label Grid.Row="0" Grid.Column="1" Content="{Binding TimeStamp}">
<Label.ContentStringFormat>0:yyyy-MM-dd HH:mm:ss.fff</Label.ContentStringFormat>
</Label>
also see Binding only part of a label

How to bind data template of a listbox to class fields

I have a Class Person that consists of FirstName and LastName. I created an object of type ObservableCollection and filled it with some data, bounded it to Listbox.ItemsSource via code-behind. Now I want that data to be displayed on the Window inside a listbox, but via data template, so I can choose which fields of a class to appear..
So, one item would represent FirstName and Lastname in two separate textblocks.
<Window x:Class="PlayList.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:PlayList"
Title="Media Player PlayList"
Height="300"
Width="300" >
<Grid Height="224" Name="grid1" Width="261" >
<ListBox Height="100" x:Name="listBox1" Margin="12,0,12,124" MouseDoubleClick="listBox1_MouseDoubleClick" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=Surname}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Edit:
personae = new ObservableCollection<Person> { per1, per2, per3, per5, per4 }; listBox1.ItemsSource = personae;
Make sure FirstName and LastName are properties, not fields.
Setting the Items Source properly and your example template should be enough
If the ListBoxItem's data context object contains properties string FirstName and string Surname, having the following in the markup would suffice:
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding Surname}"/>
</StackPanel>
</DataTemplate>
You cannot bind to fields, only to properties (which preferably are in a class implementing INPC if you need changes to reflect in the view).

Resources