Is it possible to have multiple ItemSources for a single control?
Given the code below:
<ComboBox Margin="137,101,169,183" ItemsSource="{Binding collection}" SnapsToDevicePixels="True"
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Command="{Binding CheckCommand}" IsChecked="{Binding IsChecked}" Content="{Binding Name}"/>
<TextBlock Text="" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The TextBlock within the ComboBox DataTemplate requires data from another property within the VM than that of the ComboBox. How can this be achieved?
Thanks.
You can use RelativeSource-FindAncestor to reach up the visual tree and grab a different DataContext.
For example (assuming the command is what you want):
Command=”{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=CheckCommand}”
This should also serve as a good resource.
Edit: Typo and resources.
If i remember correctly, DataTemplates run within their own scope and cannot directly use ElementNames defined outside the DataTemplate. You could however get around it by using StaticResource and referring to that directly from TextBlock inside the template.
I haven't tried Ragepotatos's approach to go outside DataTemplate scope but would love to know if that works out for you too.
Related
I am creating a simple image editing-style app and am attempting to use MVVM for the first time. The idea is to parse proprietary text files and represent them visually in tabs within the app so they can be updated with paint strokes and the like before being converted back to text. In figuring out how to get mouse position to the view model, I found Mark Green's solution works well; however, I can't get it to work if I place it inside my TabControl.ContentTemplate - if I do so, the program crashes unceremoniously when I load the first text file (which creates the first tab). Here's the relevant section of code (see link for the code used in the ViewModels):
<Grid>
<!--If I stick the Interaction behavior here it works, but I get mouse position relative to entire grid-->
<TabControl ItemsSource="{Binding Maps}" SelectedIndex='{Binding TabIndex}'>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Image x:Name="imgMap" Source='{Binding Content}'>
<i:Interaction.Behaviors><!--Putting it here causes an unhandled exception-->
<local:MouseBehaviour MouseX="{Binding PanelX, Mode=OneWayToSource}"
MouseY="{Binding PanelY, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</Image>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
As you can see from my comments in the code, my placement of the interaction behavior seems to be what is breaking the app. Since the exception occurs in external code, I can't figure out exactly what is wrong but I suspect it has something to do with the code being inside a template (please correct me if I am wrong!).
My question is this: how do I get interaction behaviors (and later triggers - I will want mouse button events as well) working within the template, or am I going about this the wrong way? Is there a better solution to get mouse position (relative to the image) to the ViewModel?
Many thanks in advance for any help offered!
UPDATE
Per STrenat's suggestion, I updated the MouseBehavior class to use UIElement instead of Panel:
public class MouseBehaviour : System.Windows.Interactivity.Behavior<UIElement>
{
// Rest of code from Mark Green's solution linked in original post
}
To get access to the mouse position values in the main window (instead of inside the ItemTemplate), I updated my bindings in the XAML (with output text blocks now shown) to point to properties in the main viewmodel (without the relative reference you have to have the properties inside the TabItem):
<StackPanel>
<TextBlock Text="{Binding PanelX, StringFormat='X={0:0}, '}" />
<TextBlock Text="{Binding PanelY, StringFormat='Y={0:0}'}" />
<TabControl ItemsSource="{Binding Maps}"
SelectedIndex='{Binding TabIndex}'>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Image x:Name="imgMap"
Source='{Binding Content}'>
<i:Interaction.Behaviors>
<local:MouseBehaviour MouseX="{Binding DataContext.PanelX, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=OneWayToSource}"
MouseY="{Binding DataContext.PanelY, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</Image>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
The rest is as specified in the solution referenced above. Hope that helps anybody else running into this problem!
To display items on Telerik:RadGridView usually I use DataContext="{Binding [someViewModel]}" and ItemSource="{Binding objectList, Mode=TwoWay}".
and for my column I'll access the objectfield. The overall picture will be something like below:
<telerik:RadGridView DataContext="{Binding [someViewModel]}"
ItemSource="{Binding objectList, Mode=TwoWay}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn>
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<CheckBox IsEnabled="{Binding enabledVar}"
IsChecked="{Binding isChecked, Mode=Twoway}"
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
Imagine there will be 10 items in objectList. Each of the item in objectList will have a variable isChecked which to manipulate the checkbox IsChecked property.
I have another variable in the same viewmodel named enabledVar which to control the ten checkbox IsEnabled property. enabledVar is not part of the objectList but I couldn't get the value. May I know how to handle such case?
Updates:
I've found some new direction but not sure if it helps.
<CheckBox IsEnabled="{Binding enabledVar,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=telerik:RadGridView}}"
but then of course, still failed.
Any helps would be very much appreciated.
if using Ancestor you have to bind to DataContext
<CheckBox IsEnabled="{Binding Path=DataContext.enabledVar,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=telerik:RadGridView}}"
Another aproach could be to use element binding set name
<telerik:RadGridView x:Name="myGrid" ...
And then in the celltemplate bind to it
<CheckBox IsEnabled="{Binding Path=DataContext.enabledVar, ElementName=myGrid}"
Hope this helps.
The code suggested in Update will not work because GridViewDataColumn is not part of the Visual tree, and hence can not access DataContext.
You will have to make use of proxy data binding, like suggested here. You can get more examples by searching "wpf proxy databinding".
I am having problems binding a textbox to my viewmodel.
<DataTemplate x:Key="ContentDetail" >
<StackPanel Orientation="Horizontal" Height="500"">
<TextBlock TextWrapping="Wrap" Text="{Binding SelectedCall.CUCODE }" />
</StackPanel>
</DataTemplate>
I know the binding is fine as I have it also bound outside the datatemplate
DataContext="{Binding HelpdeskViewModel, Source={StaticResource ServiceLocator}}"
dx:ThemeManager.ThemeName="VS2010" SelectedItem="{Binding SelectedCall,UpdateSourceTrigger=PropertyChanged}">
Any pointers would be gratefully accepted.
Edit:
<dxg:GridControl.DetailDescriptor>
<dxg:TabViewDetailDescriptor>
<dxg:TabViewDetailDescriptor.DetailDescriptors>
<dxg:ContentDetailDescriptor ContentTemplate="{StaticResource ContentDetail}" HeaderContent="More Detail" >
</dxg:ContentDetailDescriptor>
</dxg:TabViewDetailDescriptor.DetailDescriptors>
</dxg:TabViewDetailDescriptor>
</dxg:GridControl.DetailDescriptor>
Items within a Template are bound to the current item in the Template (so your datacontext in this scope isn't your window's viewmodel, but the current item).
I assume SelectedCall is a property on your window's viewmodel and not a property on each bound item, so you can't access that. If it's also a property of each model then simply bind to CUCODE, else if it's a single per window item, you'd have to trace back to the ancestor window & bind to the datacontext of the window instead of the one automatically set for you within the context of the Template.
You're probably looking for something like that
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=SelectedCall.CUCODE }}" />
I've built a WPF based Treeview with
Item
-Subitem
If Subitem is selected, I would like to display also Properties of Item.
<StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
<TextBox Text="{Binding RelativeSource={???} Path=Name, Mode=TwoWay}" />
</StackPanel>
I guess I need to use a RelativeSource statement, but not quite sure how to do so.
{Binding RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}, Path=Name, Mode=TwoWay}
JesseJames has given you the correct way to use RelativeSource but the best you will be able to do with RelativeSource is bind to the TreeViewItem itself, which is just the container for your data object i.e ViewModel, meaning you won't be able to access your data objects properties(easily).
I think in this case binding to the container object would break the View-ViewModel approach you are using. Your best bet would be to create a Parent object within your ViewModel and bind to that object. So that now each object in your collection has a reference to it's parent which can now be bound to directly.
<StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
<TextBox Text="{Binding Parent.Name}" />
</StackPanel>
Also note that the SelectedItem property returns your data object and not the container.
I looked at your code, try binding simply to Name. It appears that your data context should already be set to the TreeViewItem due to the following line: <StackPanel Grid.Column="2" DataContext="{Binding ElementName=myTreeView, Path=SelectedItem}">. The RelativeResource binding is probably looking further up the logical tree and that's why your binding is failing.
I have a listbox and I have set the itemstemplate as shown below.
XAML:
<ListBox ItemsSource="{Binding DataList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="CheckBox" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock x:Name="TextBlock" Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,10" FontSize="26.667" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to get which all check box has been selected.Is there any way to get checkbox control for each item so that I can check its IsChecked property.
I can think of a way of binding the IsChecked property.But Is there any other way to do it?
Yes. One way to do is to bind the IsChecked property. And if you are using MVVM, probably that's the right way to do it.
Anyways, if you don't want to go the binding way, and assuming you want to iterate over all items of listbox, and prepare a list of checked items, see if this helps:
WPF - Find a Control from DataTemplate in WPF
If you're already binding to the Title property in the item template then it would certainly make sense to bind to IsChecked, too.
If you really need to, you can walk the visual tree by using the VisualTreeHelper to find the CheckBox instances.
Binding the IsChecked property to a boolean property on your object instance contained within DataList would be the simplest and cleanest way. Alternatively, if you want to avoid code behind, then you could write an attached property.
See also How to access a specific item in a Listbox with DataTemplate?
I bet it cannot be simpler than that:
<ListBox SelectionMode="Multiple" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
Content="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>