I've a main view and many usercontrols.
The main view contains a two column grid, with the first column filled with a listbox whose datatemplate consists of a usercontrol and the second column filled with another usercontrol. These two usercontrols have the same datacontext.
MainView:
<Grid>
//Column defs
...
<ListView Grid.Column="0" ItemSource="{Binding FooList}">
...
<DataTemplate>
<Views: FooView1 />
</DataTemplate>
</ListView>
<TextBlock Text="{Binding FooList.Count}" />
<StackPanel Grid.Column="1">
<Views: FooView2 />
</StackPanel>
<Grid>
FooView1:
<UserControl>
<TextBlock Text="{Binding Foo.Title}">
</UserControl>
FooView2:
<UserControl>
<TextBlock Text="{Binding Foo.Detail1}">
<TextBlock Text="{Binding Foo.Detail2}">
<TextBlock Text="{Binding Foo.Detail3}">
<TextBlock Text="{Binding Foo.Detail4}">
</UserControl>
I've no IDE here. Excuse me if there is any syntax error
When the user clicks on a button. These two usercontrols have to be replaced by another two usercontrols, so the datacontext changes, the main ui remaining the same.
ie, FooView1 by BarView1 and FooView2 by BarView2
In short i want to bind this view changes in mainview to my command (command from Button)
How can i do this?
Also tell me if i could merge the usercontrol pairs, so that only one view exists for each viewmodel
ie, FooView1 and FooView2 into FooView and so on...
Though I am still not sure whether I got you, I suggest the following.
In your example it looks like you want to show the details of the Foo object. The datacontext stays the same. Maybe you can set Visibility-Flags in your viewmodel to decide what you want to display. This can be done using the command that is executed by your button.
FooView:
<UserControl>
<TextBlock Text="{Binding Foo.Title}"
Visibility="{Binding ShowTitle}">
<TextBlock Text="{Binding Foo.Detail1}"
Visibility="{Binding ShowDetails}">
<TextBlock Text="{Binding Foo.Detail2}"
Visibility="{Binding ShowDetails}">
<TextBlock Text="{Binding Foo.Detail3}"
Visibility="{Binding ShowDetails}">
<TextBlock Text="{Binding Foo.Detail4}"
Visibility="{Binding ShowDetails}">
</UserControl>
Is this a possible solution?
If you want to change the datatemplate you might do something like this:
FooView (2nd version)
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="dataTemplate1">
<TextBlock Text="{Binding Foo.Title}">
</DataTemplate>
<DataTemplate x:Key="dataTemplate2">
<TextBlock Text="{Binding Foo.Detail1}">
<TextBlock Text="{Binding Foo.Detail2}">
<TextBlock Text="{Binding Foo.Detail3}">
<TextBlock Text="{Binding Foo.Detail4}">
</DataTemplate>
</UserControl.Resources>
<Style x:Key="Default">
<Style.Triggers>
<DataTrigger Binding="{Binding Foo.ShowFooView}" Value="1" >
<Setter Property="Template" Value="{StaticResource dataTemplate1}" />
<DataTrigger>
<DataTrigger Binding="{Binding Data.ShowFooView}" Value="2" >
<Setter Property="Template" Foo="{StaticResource dataTemplate2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl>
The idea is, that depending on the property Foo.ShowFooView you decide which data template should be used. Of course the type Foo should implement INotifyPropertyChanged to notify the UI.
Sorry, but I don't get the point. To answer your question you need to provide more information.
Do you want to change your datacontext by replacing a usercontrol?
Why don't you use different datatemplates for different target types?
Maybe you can provide some code snippets.
Related
I'm trying to use a UserControl-derived control (UpdateBlockControl) inside the DataTemplate of a ListView, like this:
<ListView x:Name="AppsListView">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:AppBase">
<StackPanel Orientation="Horizontal">
<local:UpdateBlockControl ImageSource="{x:Bind ImageSource}" AppName="{x:Bind Name}"/>
<TextBlock Text="{x:Bind Name}" Foreground="Orange" Margin="8,0,0,0" />
<TextBlock Text="{x:Bind ImageSource}" Margin="8,0,0,0" Foreground="LimeGreen"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
UpdateBlockControl is derived from UserControl and I'm trying to bind its properties (ImageSource and AppName) to the model's (AppBase) properties.
You can see me trying to set up these bindings in the first item in the StackPanel. Just to see if the binding works at all, I also bind a couple of TextBlocks to these properties.
The XAML for UserBlockControl is essentially this:
<StackPanel Orientation="Vertical">
<TextBlock Text=":-)"/>
<TextBlock Text="{x:Bind AppName}"/>
<TextBlock Text="{x:Bind ImageSource}"/>
</StackPanel>
When I run the application I see the following:
So the UserControl properties aren't working correctly it would seem: I get the :-) for the first TextBlock but the second and third TextBlocks are empty. Their equivalents in the ListView do work however (see image, above).
Should I be able to use a user control insisde a DataTemplate like this? If so, why aren't the bindings working?
x:Bind is Mode=OneTime by default. So, once a DependencyProperty is initialized inside the UserControl, it won't be updated anymore.
You need to add Mode=OneWay to x:Bind in UpdateBlockControl.
<StackPanel Orientation="Vertical">
<TextBlock Text=":-)" />
<TextBlock Text="{x:Bind AppName, Mode=OneWay}" />
<TextBlock Text="{x:Bind ImageSource, Mode=OneWay}" />
</StackPanel>
I have a ListBox that presents a databound list of objects via its ItemSource. Because each object has special display needs I’m defining an ItemTemplateSelector that returns the appropriate DataTemplate depending on the object. That all works without a hitch.
The DataTemplates for each object follow a common formula, but contains custom elements in the middle. For example:
<DataTemplate x:Key="collectibleTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1">
<Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke">
<StackPanel>
<TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" />
<!-- This is the only custom part of each template -->
<StackPanel Margin="0,10,5,0" Orientation="Horizontal">
<Label Content="Type:" />
<ComboBox Height="22" HorizontalAlignment="Left" SelectedItem="{Binding Path=CollectibleType, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource collectibleTypeFromEnum}}" />
</StackPanel>
<!-- End custom part -->
<StackPanel Margin="0,0,0,5">
<Label Content="Available Actions:" >
<Label.Style>
<Style TargetType="Label">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding EditActions.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<ItemsControl ItemsSource="{Binding EditActions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding}" Content="{Binding Title}" ToolTip="{Binding ToolTip}" Margin="5,0,5,0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Expander>
</Border>
</Grid>
</DataTemplate>
As you can see there’s lots of shared XAML, wrapping a small custom section in the middle.
Additional data templates will be written by other engineers (they’ll want to create one for each new object type that they add), so I’m interested in making the creation of a new DataTemplate as fool-proof and painless as possible. No copying of the entire DataTemplate with the custom “stuff” added in the middle, of course – but I’m also not partial to extracting parts of the template as reusable parts and referencing them in because it still leads to lots of duplicate code in each new DataTemplate, and that means possible errors and hard maintainability. I.e., this right here is a more maintainable approach but still feels suboptimal:
<DataTemplate x:Key="collectibleTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="LightGray" BorderThickness="1">
<Expander IsExpanded="True" Header="{Binding ComponentName}" Background="WhiteSmoke">
<StackPanel>
<TextBlock Margin="5,5,5,0" Text="{Binding EditDescription}" TextWrapping="Wrap" />
<!-- This is the only custom part of each template -->
[...]
<!-- End custom part -->
<ContentPresenter Content="{StaticResource AvailableActions}" />
</StackPanel>
</Expander>
</Border>
</Grid>
</DataTemplate>
<StackPanel Margin="0,0,0,5" x:Key="AvailableActions" x:Shared="false">
<Label Content="Available Actions:" >
<Label.Style>
<!--
[Bottom half of shared XAML from the first example, offloaded here]
-->
</StackPanel>
So: what is my best strategy to solve this? AFAIK I’m stuck with using DataTemplates because that’s the only element that a ListBox ItemTemplateSelector accepts. Is there a way to create a compound DataTemplate in the DataTemplateSelector? I'd provide the stock DataTemplate that is shared by all objects, and the DataTemplateSelector references in the bit of custom XAML needed for each object type. Other engineers would hook into that generalized code behavior.
Not sure, fumbling a bit in the dark here as whether there is a pattern that allows me to solve this elegantly.
And, just for reference: my current DataTemplateSelector is super straightforward. This is where I would expect to construct the final DataTemplate, rather than simply returning one that's hardcoded in XAML.
public class NodeComponentDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
if (item is CollectibleComponent)
return element.FindResource("collectibleTemplate") as DataTemplate;
// [...]
}
}
}
You could create the DataTemplate dynamically using the XamlReader.Parse or XamlReader.Load method, e.g.:
string template = "<DataTemplate xmlns =\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x =\"http://schemas.microsoft.com/winfx/2006/xaml\"><StackPanel>[PLACEHOLDER]</StackPanel></DataTemplate>".Replace("[PLACEHOLDER]", "...custom code...");
return System.Windows.Markup.XamlReader.Parse(template) as DataTemplate;
The custom parts could be defined as UserControls.
I am afraid there is no way to base a DataTemplate on another one in pure XAML though.
You could create a new CustomControl that fits your needs. It will apply the style by itself and you can give additional DepdendencyProperties to make it more convinient. In the end you can still put it in a DataTemplate to use it with your DataTemplateSelector.
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ElementName=txbValueY,Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
I try this above code but i cant to bind the text, how can i bind inside resources textblock text to outside the resources, Thanks
I am guessing that you are trying to show Text present in TextBlock resource in your second non-resource TextBlock.
You don't need DataTemplate. As you will progress ahead in WPF journey, you will come to know about those.
Below code will show "Resource Text" in your second TextBlock.
<Grid.Resources>
<TextBlock x:Key="TbRes1" Text="Resource Text" x:Name="txbValueY" Margin="5" FontSize="11" FontWeight="Medium"/>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource TbRes1},Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
All sorts of problems here:
You're specifying Mode.TwoWay in your TextBlock Text binding, it should be Mode.OneWay.
You're binding to the Label's Text property. Label doesn't have a Text property, only Content. And it's not a dependency property so you can't bind to it. (That said, a fluke of the internal mechanics does cause it to appear to work under certain conditions).
A template is exactly that: a template. You can't bind to something that doesn't exist, so the binding is meaningless.
Maybe you could clarify exactly what it is you're trying to do so we can suggest an alternative way of achieving it? Specifically, show us exactly how you're instantiating that DataTemplate.
UPDATE:
You need the first textbox to be created in order for the second one to bind to it, simply declaring it inside a DataTemplate doesn't cause that to happen by itself, so the direct binding will fail. Binding UI elements together like this should generally be avoided though, why can't you simply give the second textbox the same binding as the first?
<Grid>
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ValueX}" Background="Orange" Foreground="White"/>
</Grid>
If for some reason this isn't possible then you can also create a binding proxy object (see this page for details):
<Grid>
<Grid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding ValueX}" />
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource ResourceKey=proxy}, Path=Data}" Background="Orange" Foreground="White"/>
</Grid>
Again, there are ways to bind to the data template declaration if you really want to, but to do that I'd have to see details of how your data template is being created at runtime.
I have a combobox in wpf which is binded to some property (another object). Because I need to show two properties of that object i used DataTemplate inside combobox. Now when combobox is in focus I can't select some value by typing few starting letters (without DataTemplate it is possible).
<ComboBox Height="23" HorizontalAlignment="Left" Margin="104,14,0,0" Name="tipDokumentaCombo" VerticalAlignment="Top" Width="241" TabIndex="0" ItemsSource="{Binding Path=TipoviDokumenta}" SelectedValue="{Binding Path=Potvrda.Tip}" SelectedValuePath="Tip" SelectionChanged="tipDokumentaCombo_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Tip}" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Path=OpisDokumenta}" />
<TextBlock Text=")" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Set TextSearch.TextPath to the property that should be searched.
Update
Because the abvove solution seems not to work for you, try to set the search text manually for the container:
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="TextSearch.Text" Value="{Binding Tip}" />
</Style>
</ComboBox.ItemContainerStyle>
Add DisplayMemberPath to the property on which lookup should work on.
Setting DisplayMemberPath worked for me even when itemtemplate is present.
Based on the comment discussion, the solution that works is to add both IsTextSearchEnabled="True" and TextSearch.TextPath="Tip" in the ComboBox tag.
For example (re-writting the question code sample - removing some not useful to the example code to reduce complexity)
<ComboBox Name="tipDokumentaCombo" TabIndex="0" ItemsSource="{Binding Path=TipoviDokumenta}" SelectedValue="{Binding Path=Potvrda.Tip}" SelectedValuePath="Tip" SelectionChanged="tipDokumentaCombo_SelectionChanged" IsTextSearchEnabled="True" TextSearch.TextPath="Tip">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Tip}" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Path=OpisDokumenta}" />
<TextBlock Text=")" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Disclaimer: I am not the original author. This answer is fabricated from the existing comments. Attribution should be given to #mersadk who post, most of this answer's details in the comments. I test it and verified that this works in my (similar) environment - issue.
I've a ListView bound to an ObservableCollection<Foo>. On selecting a ListViewItem I display the details of the SelectedItem(Foo members) in a container. It works fine.
Now all I want is to display the details just next to the SelectedItem. ie, If I select the fourth ListViewItem then the Container's Top should be the same as the ListViewItem's Top. How would I sync their position provided it should create any problem even while scrolling the List.
P.S: Scrollbars are hidden
This question is not yet resolved. Can anybody help?
Original Answer
Does the detail need to be in a separate container? I may be misunderstanding your example, but I would have thought you could achieve what you wanted by adding the details section to the item template for your list items and then hiding/showing it based on the IsSelected flag:
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel>
<ContentControl DockPanel.Dock="Right" Name="DetailsControl" Content="{Binding}" ContentTemplate="{StaticResource DetailsTemplate}" />
<TextBlock Text="{Binding}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding (ListViewItem.IsSelected), RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}" Value="False">
<Setter TargetName="DetailsControl" Property="Visibility" Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Even if you aren't after exactly this behaviour, I imagine you could get close to what you want by replacing the ContentControl from the example with something else (e.g. a Popup)
Edit in response to comments
The example below will place a Popup to the right hand side of the ListView that is visible only for the selected item:
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding}" />
<Popup Placement="Right"
PlacementTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}"
IsOpen="{Binding (ListViewItem.IsSelected), RelativeSource={RelativeSource FindAncestor, AncestorType=ListViewItem}}">
<Border Background="Black" Padding="3">
<TextBlock Text="{Binding}" />
</Border>
</Popup>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This uses the Placement attribute to specify that the Popup should appear to the right of the target element, and binds the PlacementTarget property to the first ancestor of type ListViewItem (i.e. the parent container).
This causes an appearance like the example below: