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 }}" />
Related
This question already has an answer here:
How do I bind a close button in a TabControl header to a command in the window's ViewModel?
(1 answer)
Closed 6 years ago.
I have a DataTemplate:
<DataTemplate x:Key="BMSelectedItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="*" Visibility=???/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
I have a DataTemplateSelector using the above template:
<BookmarkItemDataTemplateSelector x:Key="BookmarkItemDataTemplateSelector" SelectedItemTemplate="{StaticResource BMSelectedItemTemplate}"
DropdownItemsTemplate="{StaticResource BMDropdownItemTemplate}" />
I use the above data template selector in a comboBox:
<StackPanel x:Name="splBookmark" Visibility="{Binding ShowBookmark, Converter={StaticResource BooleanToVisibilityConverter}}">
<ComboBox x:Name="cbBookmark" ItemTemplateSelector="{StaticResource BookmarkItemDataTemplateSelector}"/>
</StackPanel>
My view model has properties ShowBookmark and ShowAsterisk. I want to bind visibility of the "*" in BMSelectedItemTemplate to my view model's property ShowAsterisk. How could I do that? I tried:
Visibility="{Binding ShowAsterisk, Converter={StaticResource BooleanToVisibilityConverter}}"
But it didn't work, it said data template cannot find property ShowAsterisk, I think this makes sense, because the data template is binding to a list of MBookmark objects, and in my MBookmark class, there is no property of ShowAsterisk. ShowAsterisk is a property of the view model which is binding to splBookmark stack panel.
My question is how to bind ancestor element's view model property to my data template element's visibility?
I cannot use relative path ancestor type to find my comboBox or stack panel, it seems I can only use self or TemplateParent in my relative source. I use silverlight.
Thank you!
In WPF, you can use RelativeSource to climb up the visual tree.
Assuming your items container is a ListBox:
Visibility="{Binding RelativeSource={RelativeSource AncestorType=ListBox},
Path=DataContext.ShowAsterisk,
Converter={StaticResource BooleanToVisibilityConverter}}"
In a UWP app, where RelativeSource is not available, you can use ElementName instead.
First, name the items control:
<ComboBox Name="MyComboBox" />
Then, use it in the binding:
Visibility="{Binding ElementName=MyComboBox,
Path=DataContext.ShowAsterisk,
Converter={StaticResource BooleanToVisibilityConverter}}"
My WPF Windows contains a TabControl which displays content on different tabs. A click on the button below executes a method via ICommand interface / Binding. The called method generates text which is intended to be displayed in the second tab.
How can I switch to the second tab on button click without violating the MVVM Pattern?
I tried to bind the TabItem.IsSelected Property to something in my ViewModel but I wanted to use the other tabs (tab1) as well.
Any thoughts?
I found it out by myself.
The key is a two way binding. When the button is clicked it sets the property DisplayXamlTab true. The IsSelected attribute is bound to this variable. if another tab is clicked the binding will set the DisplayXamlTab Property to false.
Note: UpdateSourceTrigger=PropertyChanged is also very important
Code comes below:
XAML:
<TabItem Header="XAML" IsSelected="{Binding DisplayXamlTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Grid Background="#FFE5E5E5">
<TextBox x:Name="TxtXamlOutput" IsReadOnly="True" Text="{Binding XamlText, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
</Grid>
</TabItem>
C# Property:
private bool displayXamlTab;
public bool DisplayXamlTab
{
get { return this.displayXamlTab; }
set
{
this.displayXamlTab = value;
this.RaisePropertyChanged("DisplayXamlTab");
}
}
if you're going the MVVM way you're going to create two dependency properties in the code behind:
ObservableCollection<ItemType> Items;
ItemType MySelectedItem;
Then, bind the TabControl ItemsSource property to the Items and bind the SelectedItem property to MySelectedItem
<TabControl ItemsSource="{Binding Items}"
SelectedItem="{Binding MySelectedItem, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<... here goes the UI to display ItemType ... >
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
When you want to change the selected tab, simply update the MySelectedItem dependecy property
Although this question is fairly old and well answered already, I thought I'd add this additional answer to demonstrate an alternative way of changing the selected TabItem in a TabControl. If you have a view model for each TabItem, then it can be helpful to have an IsSelected property in it to determine whether it is selected or not. It is possible to data bind this IsSelected property with the TabItem.IsSelected property using the ItemContainerStyle property:
<TabControl ItemsSource="{Binding MenuItems}" TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Margin="0,0,10,0" />
<TextBlock Text="{Binding HeaderText}" FontSize="16" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
<ContentControl Content="{Binding ViewModel}" />
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
You can now change the selected TabItem from the parent view model like this:
MenuItems[0].IsSelected = true;
Note that because this property is data bound to the TabItem.IsSelected property, calling this...:
MenuItems[1].IsSelected = true;
... will infact also automatically set the MenuItems[0].IsSelected property to false. so if the view model that you are working with has its IsSelected property set to true, then you can be sure that its related view is selected in the TabControl.
You can create a binding between the view model and the TabControl.SelectedIndex property - i.e., 0 selects the first TabItem , 1 selects the second, etc.
<TabControl DataContext="..." SelectedIndex="{Binding SomeVmProperty}" ...
(alternatively, depending on how you've got things set up, you could bind against SelectedItem...)
You'll likely want to use some sort of "Event Aggregator" pattern (I.e. the Messenger class in MVVM Light) to broadcast some sort of "navigation" message. Your View - the TabControl - can listen for the specific message, and navigate to Tab2 when the message is received.
Alternatively, you can bind the "SelectedItem" property of the TabControl to your ViewModel, and simply call CurrentTab = MySecondTabViewModel from within your VM. This is the approach recommended by #HighPoint in the comments to the OP, but I'm not a fan; see below. Another caveat to this approach is that you need to be familiar with DataTemplates, as you will need to map a view to each ViewModel which you display.
I personally like the first approach, because I don't consider it to be a "responsibility" of the ViewModel to handle tab navigation. If you simply alert your View when data changes in your ViewModel, you allow the View to decide whether or not it wants to change tabs.
I'm interested in creating an app that displays some buttons and changes a viewport according to the selected button. The viewport in my app is a ContentControl and I thought of changing its content whenever a button is clicked. However, I believe there's a better approach, by perhaps injecting the ViewModels of each of the Views I want to present to the ContentControl and styling them using DataTemplates (Since I want to avoid having a grid with many controls and just setting their Visibility property whenever I want to show a particular view). Which of the approaches seems better to you? Do you have a different approach for this?
The view should be something similar to this:
Thanks!
Usually have a ViewModel behind the window which contains:
ObservableCollection<IViewModel> AvailableViewModels
IViewModel SelectedViewModel
ICommand SetCurrentViewModelCommand
I display the AvailableViewModels using an ItemsControl, which has its ItemTemplate set to a Button. The Button.Command is bound to the SetCurrentViewModelCommand, and it passes the current data item from the AvailableViewModels collection in through the CommandParameter
To display the content area, I use a ContentControl with ContentControl.Content bound to SelectedViewModel, and DataTemplates get used to tell WPF how to render each ViewModel.
The end result is my XAML looks something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding AvailableViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.SetCurrentViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding SelectedViewModel}" />
</DockPanel>
You can view an example of the full code used for such a setup on my blog
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.
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.