Editable ItemsControl DataTemplate with MVVM pattern on WPF 4.0 - wpf

I've got problem to define items in my ItemsControls (or in my view or viewModel). May be I don't know something about mvvm, but here is the question.
I have DataTemplate defined as
<DataTemplate>
<Expander>
<Expander.Header>
<StackPanel>
<TextBlock Margin="2" Text="{Binding Name}" />
<TextBlock>
<Hyperlink>
<Run Text="Delete group" />
</Hyperlink>
</TextBlock>
</StackPanel>
</Expander.Header>
<Expander.Content>
blah-blah-blah
</Expander.Content>
</Expander>
</DataTemplate>
I have a lot of Expanders, each of them have a Hyperlink. And I want the Hyperlink to delete the group - i.e. remove corresponding Expander. I have read about searching on DataTemplate with Visual and Logical - TreeHelper, but it is a bad way to solve my problem - it doesn't follow mvvm. I know that I need to write a Commmand for the Hyperlink, but how can I retrieve Expander, which I need to delete, and satisfy mvvm - that's the question.
Any advice, any suggestion...

But, if you are can't create static ICommand property for some develop reasons, you should use RelativeSource to find ancestor, which have DataContext set to ViewModel, which have this command.
<Hyperlink Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ItemsControl},
Path=DataContext.DeleteFileGroup}"
CommandParameter="{Binding}">
<Run Text="Delete group"/>
</Hyperlink>

All what I need to do - is craete STATIC ICommand property, and use following code
<Hyperlink Command="{x:Static vm:FileGroupViewModel.DeleteGroup}" CommandParameter="{Binding}">
<Run Text="Delete group"/>
</Hyperlink>

Related

How to display too long text properly in WPF ComboBox

I have a ComboBox that shows text of various lengths. For texts that are not long there is not a problem. For the texts longer than the width of ComboBox I would like to trim the text and add "..." (an ellipsis) at the end to show them properly. The bottom line is that I don't want to change the width of the ComboBox. Does anyone know how to do this?
Use a custom ItemTemplate for your ComboBox, which makes use of a TextBlock with the TextTrimming property set to CharacterEllipsis.
Example:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding ...}"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The answer, as Ross said, is to implement a custom ItemTemplate. However, to make it work properly, you need to do the binding properly.
A note on this method: You cannot set both the DisplayMemberPath and the ItemTemplate, it must be one or the other.
So, for the general case where the display member is the item (such as for a string), you can use binding with no properties to bind to the DataContext of the template:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Or, you can put it in a style.
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
For the case where you want to bind to a specific property of the object, similar to how you would use the DisplayMemberPath property, replace the binding with the binding that you would use to a property on the object that you are binding. So, replace the fourth line in my first example with something like this:
<TextBlock Text="{Binding MyDisplayMemberProperty}" TextTrimming="CharacterEllipsis" />
The binding is in the context of a single item of the type bound to your ComboBox. To make this more explicit, you can do the following:
<DataTemplate DataType="{x:Type namespace:MyItemType}">
<!-- My DataTemplate stuff here -->
</DataTemplate>
This will give you hints for the properties on the object while you are writing code inside the DataTemplate.
You can use TextTrimming CharacterEllipsis or WordEllipsis for the textblocks in your combobox.
Also works with a more complex DataTemplate; however, I had to resort to a DockPanel instead of the standard WrapPanel.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<AccessText DockPanel.Dock="Left" Text="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Pattern for working with a form in Silverlight 4? (How to get references to XAML elements)

I have a form:
<StackPanel Orientation="Horizontal" Visibility="{Binding Editable, Converter={StaticResource visibilityConverter}}"
ToolTipService.ToolTip="Add new topic to this group">
<sdk:AutoCompleteBox Width="160" ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.TopicNames}" />
<Button Click="addTopicButton_Click">
<Image Source="Images/appbar.add.rest.png" />
</Button>
</StackPanel>
This form appears in a DataTemplate for an ItemsControl. I'm not sure what the best way is to get the data from the AutoCompleteBox when the button is clicked. I can't give the elements x:Name attributes, because they're in a template (right?).
How can I get around this? The Click event will give me the Button, but I need a reference to the text box. Use the Button's parent, then look through the children for the Textbox? If I factored this out into its own UserControl, I could set x:Name values, but I'd rather not do that.
Any other ideas?
Update: Here is another example of such a problem:
<ListBox x:Name="topicList"
ItemsSource="{Binding Id, Converter={StaticResource topicGroupIDConverter}}"
SelectionChanged="ListBox_SelectionChanged"
HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
Width="150"
VerticalAlignment="Center"
ToolTipService.ToolTip="{Binding Description}"
ToolTipService.Placement="Right" />
<Button ToolTipService.ToolTip="Remove this topic from this group"
Visibility="{Binding ElementName=topicList,
Path=DataContext.Editable,
Converter={StaticResource visibilityConverter}}"
Click="removeTopicButton_Click"
HorizontalAlignment="Right"
Margin="10,0">
<Image Source="Images/appbar.cancel.rest.png" />
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When the button is clicked, I want to access topicList.DataContext. However, topicList itself is a DataTemplate in an ItemsControl, so I can't access it using its name from code-behind. How else can I do this?
You can add a property, say SelectedItemInAutoCompleteBox, to your presenter, and then can bind it to the SelectedItem property of AutoCompleteBox, using Mode=TwoWay, like this,
<sdk:AutoCompleteBox SelectedItem="{Binding Path=DataContext.SelectedItemInAutoCompleteBox, Mode=TwoWay}" ... />
You may try the same approach with Text property of AutoCompleteBox, also. See if it solves your problem.:-)
You have several choices:
If you're on Silverlight 5, use the AncestorBinding
Otherwise, use a Silverlight 4 AncestorBinding hack (it doesn't look pretty)
Or you could try DataContextProxy, which stores the DataContext in a resource so that it is accessible. Note: you should set the DataContextProxy as a Resource of topicList ListBox, not the UserControl as in Dan Wahlin's example.

How to bind property of parent element

i have a quick Binding Question about Silverlight.
I have some Expander and want to overwrite their Header Templates
<Controls:Expander Header="MyHeaderTitle"
HeaderTemplate="{StaticResource MyExpanderHeaderTemplate}">
//Content
</Controls:Expander>
<Controls:Expander Header="MyNextHeaderTitle"
HeaderTemplate="{StaticResource MyExpanderHeaderTemplate}">
//Content
</Controls:Expander>
In the header template i have an textbox and want to bind the text to the Header of the expander.
<DataTemplate x:Key="MyExpanderHeaderTemplate">
<TextBlock Text="{Binding Path=Header}">
// some triggering stuff
</TextBlock>
</DataTemplate>
I tried some stuff with RelativeSource (Self and TemplatedParent) but nothing seems to work.
Some Ideas would be great, thx.
Take a look at RelativeSourceMode.FindAncestor
<TextBlock
Text="{Binding RelativeSource={RelativeSource RelativeSourceMode=FindAncestor, AncestorType={x:Type Controls:Expander}}}, Path=Header"/>
Just do this
<DataTemplate x:Key="MyExpanderHeaderTemplate">
<TextBlock Text="{Binding}"/>
</DataTemplate>
Thanks for the quick answers.
FindAncestor doesn't seem to work under Silverlight the Way it does in WPF (can't resolve AncestorType..)
But {Binding} or {Binding .} do the trick!
have you tried:
<DataTemplate x:Key="MyExpanderHeaderTemplate">
<TextBlock Text="{Binding .}">
// some triggering stuff
</TextBlock>
</DataTemplate>

Element Data-Binding in Silverlight

I have a template column in a DataGrid:
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
<TextBlock Text="{Binding Name,ElementName=rsAllSkills}"/>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
And in the same xaml file, I have
<riaControls:DomainDataSource QueryName="GetSkillsQuery" AutoLoad="True" x:Name="rsAllSkills">
<riaControls:DomainDataSource.DomainContext>
<domain:XXXX context/>
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
The DataSource has loaded everything successfully for sure, if I put that TextBlock out side of the DataGrid, it works; but inside the DataGrid, it doesn't load even the Name of rsAllSkills....
Could anybody give me a hint, thank you so much.
Have a dummy converter and check the binding.
What I guess is, the DataTemplate inside the CellEditingTemplate would receive the parent's DataContext, ie., DataGrid's DataContext. So, to work around this you can do one thing.
1) Bind the rsAllSkills to the Tag Property of DataGridTemplateColumn.
2) Now, Bind the TextBlock's Text property with the Tag property like,
<sdk:DataGridTemplateColumn Tag="{Binding Name,ElementName=rsAllSkills}">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
<TextBlock Text="{Binding Tag}"/>
</StackPanel>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>

How can I achieve a binding path which is bound by another binding?

I'm writing my own UserControl which displays data in a ListBox. I want to achieve something similar to a property like "DisplayMemberPath". In my example, it's "EditableMemberPath", which should determine which member will be displayed in the textbox. However, using a binding on a binding does not work.
<ListBox ItemsSource="{Binding Path=Items, ElementName=me}"
SelectedItem="{Binding Path=SelectedItem, ElementName=me}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding Converter={Binding Path=ItemToImageConverter, ElementName=me}}" />
<TextBlock Text="{Binding Path={Binding Path=EditableMemberPath, ElementName=me}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Can you help me finding a proper solution?
Best Regards
Oliver Hanappi
You can't do that directly in XAML (at least not with the built-in classes) : a Binding is not a DependencyObject, so you can't bind its properties.
However the post mentioned by Martin Harris looks promising...

Resources