I have a wpf Toolbar in a ToolbarTray inside my application which must host Buttons and ToggleButtons.
Can someone suggest me how to implement this behavior in MVVM?
The code below is what I have right now:
<ToolBarTray Margin="5,30,5,30" MinWidth="35" HorizontalAlignment="Center" Orientation="Vertical" Background="Transparent">
<ToolBar x:Name="ToolBarControl" HorizontalAlignment="Stretch" ItemsSource="{Binding ToolBarItems}" >
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" HorizontalAlignment="Stretch" Margin="0,0,0,15"
Template="{Binding ToolBarIcon}"
cal:Message.Attach="[Event Click] = [Action Select()]"
IsEnabled="True"
ToolTip="{Binding Text}"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
</ToolBarTray>
Where the Button could be a normal Button or a ToggleButton.
Thanks in Advance.
In MVVM pattern, your model class will contain the properties (all data objects) which you want to bind and display in view. So ToolBarItems collection will be part of model.
Your view basically will contain the above code you have written. And in code behind file, there will be an object of type model class as a property.
Your viewmodel can initialize the model and view objects and bind the model to view's datacontext.
Related
This is going to be difficult to explain but bear with me.
I'm creating an application in WPF using the MVVM pattern. I'm new to it which is why I'm asking this question.
I have the application set up as 3 pages or views within a window. One of these is static and is always there, the other two are a couple of small settings pages that open in the top corner over the top of everything using zindex. At the moment the menu that opens these pages uses a listbox with togglebuttons as it's template (the checked state is bound to the listbox) so that you can click to open the menu, then click the button again to close it.
In an ideal world I'd like it so that if the menu page were to lose focus (listen for a click on the static view?) the settings views close too. Also I wondered if anyone had a simpler solution for a menu that works in a similar way because at the moment it is a pretty messy solution. Here are some code samples:
<ListBox Grid.Row="0" Grid.Column="0" ItemsSource="{Binding PageViewModels}" SelectedItem="{Binding CurrentPageViewModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Margin="10,0" Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FCCC"/>
<ToggleButton
VerticalAlignment="Stretch"
Content=""
IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Settings views -->
<ContentControl Panel.ZIndex="2" Grid.Row="1" Grid.Column="0" Content="{Binding CurrentPageViewModel}"/>
<!-- Main page view -->
<ContentControl Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Width="1000" Height="700" Content="{Binding StaticPageViewModel}"/>
I'm using the concepts in this blog post to manage my views and viewmodels, however I changed the way the menu is shown so I could remove the need for a change page command/ICommand.
TL;DR : I'm looking for suggestions and criticism with what I could do to improve the way I've currently created my menu bar.
I would create an attached property if you really wanna do the MVVM style to close the View when it loses focus.
public static readonly DependencyProperty CloseViewOnLostFocusProperty =
DependencyProperty.RegisterAttached("CloseViewOnLostFocus", typeof (object), typeof (MainWindow), new FrameworkPropertyMetadata(default(object), RegisterLostFocus));
private static void RegisterLostFocus(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
//This is the control that is attached to it e.g. ContentControl
var sender = dependencyObject as FrameworkElement;
//Your ViewModel
var viewModel = dependencyPropertyChangedEventArgs.NewValue as ViewModel;
if (sender != null)
{
sender.LostFocus += (o, args) =>
{
//Close whatever you are doing right now to close the View.
viewModel.Close();
};
}
}
And on your view you can attach whatever ViewModel you want to close when it loses focus e.g. your SettingsView got LostFocus it'll close that view. In here I created an attached property on my MainWindow class.
<!-- Settings views -->
<ContentControl
MainWindow.CloseViewOnLostFocus="{Binding RelativeSource={RelativeSource Self},Path=Content}"
x:Name="SettingsView" Panel.ZIndex="2" Grid.Row="1" Grid.Column="0" Content="{Binding CurrentPageViewModel}"/>
<!-- Main page view -->
<ContentControl x:Name="MainPageView" Grid.Row="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Width="1000" Height="700" Content="{Binding StaticPageViewModel}"/>
You can have a ContextMenu attached to the button, which is opened when the button is clicked. This way, the close-when-unfocused behaviour is entirely automatic.
You can then just restyle the menu to look however you want.
I have a data template in WPF that is attached to a ResultsViewModel class and renders the VM in a table format. A bunch of these make up ListBoxItems in a ListBox. What I want is in each individual table to have a little X at the top right of the border where if you click it, it calls a function that removes that item from the listbox.
I have tried with a Hyperlink and event OnClick but then I have to have the DataTemplate in the main XAML and not in a resource dictionary as it needs a x:Class tag to use events, but then the event gets fired in the MainViewModel, which isn't the worst thing in the world as the observable list is held in the MainViewModel and needs to be removed at that point anyway, but I can't figure out how to get a reference to the ResultsViewModel of the list box item that contained the data template that was clicked
<DataTemplate x:Key="ErroredResultsTemplate" DataType="x:Type vm:ResultsViewModel" >
<Border x:Name="Border" BorderBrush="{StaticResource ResultProcessedBorder}" Background="{StaticResource ResultFill}" BorderThickness="4" CornerRadius="10" Margin="6" Padding="5" Width="110" Height="110">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="83" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" HorizontalAlignment="Right" VerticalAlignment="Top">
<Hyperlink Click="Close_Results">X</Hyperlink>
</TextBlock>
<TextBlock Width="90" Text="An error occurred calculating results" TextWrapping="Wrap" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
You can achieve this in two ways:
Create a property of type ResultsViewModel in your parent view model (that contains your collection of ResultsViewModel objects) and bind that to the SelectedItem property of your ListBox. Add some kind of RelayCommand to the parent view model to handle the delete action, add a Button to your DataTemplate and bind its Command property to the new command. Then, when any delete button is clicked, you can just remove the item found in the SelectedItem property from your collection and the UI should update accordingly (assuming that you've implemented the INotifyPropertyChange interface).
You can simply bind from the DataTemplate of each item in the ListBox to the parent view model directly. This assumes that you have a Command in your parent view model named Delete and that the parent view model is bound to the DataContext property of the Window or UserControl that the ListBox appears in. Also note the important CommandParameter="{Binding}" part which passes the data object from each item in the collection to the object parameter in the Command when a Command is called.
Example:
<Button Content="X" Command="{Binding DataContext.Delete,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type
XmlNameSpace:WindowOrUserControlName}}, Mode=OneWay}"
CommandParameter="{Binding}" />
I have a TreeView control on my WPF window. I am giving only relevant XAML from my window.
<Window.Resources>
<HierarchicalDataTemplate x:Key="HierarchicalTemplate" ItemsSource="{Binding SubOrgUnitItems}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding ImagePath}" Stretch="Fill" Width="16"/>
<TextBlock Text="{Binding OrgUnitName}" Name="treeText" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView Margin="10,35,10,10" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto"
IsTabStop="True" Name="orgTreeView" ItemsSource="{Binding}" ItemTemplate="{DynamicResource HierarchicalTemplate}" TabIndex="700" SelectedItemChanged="orgTreeView_SelectedItemChanged" />
When the Collection of Organisations is bound to DataContext of the TreeView, items are displayed with the OrgUnitName's value as a text at every node.
Now at run time I want to see some other property's value as a text at every node. e.g. OrgUnitCode instead of OrgUnitName. Both are properties declared in the view model class associated with the treeview.
How can i do it programatically at run time?
You should use HierarchicalDataTemplateSelector.
Define two different HierarchicalDataTemplate (as you did).
Inherite your custom selector class from DataTemplateSelector, override its SelectTemplate method and put there the logic of the selection. This method will return the correct template in each case.
Create a Resource(Custom Selector class) in the xaml file.
Set the TreeViews ItemTemplateSelector to the static selector resource.
See a simple example here: Link
I have achieved what I wanted to do, but unfortunately by some work around. Following thing worked for me but it may not be the correct answer to the problem.
I added one more HirerachicalDataTemplate and TreeView. The new template uses the OrgUnitCode property. The new tree view uses the new template.
<HierarchicalDataTemplate x:Key="HierarchicalTemplateUsingCode" ItemsSource="{Binding SubOrgUnitItems}">
<StackPanel Orientation="Horizontal">
<Image Height="16" Source="{Binding ImagePath}" Stretch="Fill" Width="16"/>
<TextBlock Text="{Binding OrgUnitCode}" Name="treeText" />
</StackPanel>
</HierarchicalDataTemplate>
<TreeView Margin="10,35,10,10" BorderThickness="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Auto"
IsTabStop="True" Name="orgTreeViewCode" ItemsSource="{Binding}" ItemTemplate="{DynamicResource HierarchicalTemplateUsingCode}" TabIndex="700" SelectedItemChanged="orgTreeViewCode_SelectedItemChanged" Visibility="Hidden"/>
At run time, when I want to see OrgUnitCode property value as a text at the node, I simply make new tree visible and hide the first one (mentioned in question). So making tree views visible/invisible help me to achieve what I wanted to do.
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.
What is difference between a ControlTemplate and a DataTemplate in WPF?
Typically a control is rendered for its own sake, and doesn't reflect underlying data. For example, a Button wouldn't be bound to a business object - it's there purely so it can be clicked on. A ContentControl or ListBox, however, generally appear so that they can present data for the user.
A DataTemplate, therefore, is used to provide visual structure for underlying data, while a ControlTemplate has nothing to do with underlying data and simply provides visual layout for the control itself.
A ControlTemplate will generally only contain TemplateBinding expressions, binding back to the properties on the control itself, while a DataTemplate will contain standard Binding expressions, binding to the properties of its DataContext (the business/domain object or view model).
Very basically a ControlTemplate describes how to display a Control while a DataTemplate describes how to display Data.
For example:
A Label is a control and will include a ControlTemplate which says the Label should be displayed using a Border around some Content (a DataTemplate or another Control).
A Customer class is Data and will be displayed using a DataTemplate which could say to display the Customer type as a StackPanel containing two TextBlocks one showing the Name and the other displaying the phone number. It might be helpful to note that all classes are displayed using DataTemplates, you will just usually use the default template which is a TextBlock with the Text property set to the result of the Object's ToString method.
Troels Larsen has a good explanation on MSDN forum
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ButtonContentTemplate">
<StackPanel Orientation="Horizontal">
<Grid Height="8" Width="8">
<Path HorizontalAlignment="Stretch"
Margin="0,0,1.8,1.8"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M0.5,5.7 L0.5,0.5 L5.7,0.5"/>
<Path HorizontalAlignment="Stretch"
Margin="2,3,0,0"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M3.2,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.2,1.4,0.7,0.7"
VerticalAlignment="Stretch" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000"
Data="M2.5,2.5 L7.5,7.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.7,2.0,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M3,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1,1,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M1.5,6.5 L1.5,1 L6.5,1.5"/>
</Grid>
<ContentPresenter Content="{Binding}"/>
</StackPanel>
</DataTemplate>
<ControlTemplate TargetType="Button" x:Key="ButtonControlTemplate">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="1"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="2"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="3"/>
</StackPanel>
</Window>
(Templates blatently stolen from
http://msdn.microsoft.com/en-us/library/system.windows.controls.controltemplate.aspx
and
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.contenttemplate%28VS.95%29.aspx
respectively)
Anyway, the ControlTemplate decides how the Button itself looks, while
the ContentTemplate decides how the Content of the button looks. So
you could bind the content to one of you data classes and have it
present itself however you wanted it.
ControlTemplate: Represents control style.
DataTemplate: Represents data style(How would you like to show your data).
All controls are using default control template that you can override through template property.
For example
Button template is a control template.
Button content template is a data template
<Button VerticalAlignment="Top" >
<Button.Template>
<ControlTemplate >
<Grid>
<Rectangle Fill="Blue" RadiusX="20" RadiusY="20"/>
<Ellipse Fill="Red" />
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="50">
<TextBlock Text="Name" Margin="5"/>
<TextBox Text="{Binding UserName, Mode=TwoWay}" Margin="5" Width="100"/>
<Button Content="Show Name" Click="OnClickShowName" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
public String UserName
{
get { return userName; }
set
{
userName = value;
this.NotifyPropertyChanged("UserName");
}
}
ControlTemplate - Changing the appearance of element. For example Button can contain image and text
DataTemplate - Representing the underlying data using the elements.
ControlTemplate DEFINES the visual appearance, DataTemplate REPLACES the visual appearance of a data item.
Example: I want to show a button from rectangular to circle form => Control Template.
And if you have complex objects to the control, it just calls and shows ToString(), with DataTemplate you can get various members and display and change their values of the data object.
All of the above answers are great but there is a key difference that was missed. That helps make better decisions about when to use what. It is ItemTemplate property:
DataTemplate is used for elements that provide ItemTemplate property for you to replace its items' content using DataTemplates you define previously according to bound data through a selector that you provide.
But if your control does not provide this luxury for you then you still can use a ContentView that can display its content from predefined ControlTemplate. Interestingly, you can change the ControlTemplate property of your ContentView at runtime. One more thing to note that unlike controls with ItemTemplate property, you cannot have a TemplateSelector for this (ContentView) control. However, you still can create triggers to change the ControlTemplate at runtime.