Navigation between page - c#

I'm writing a small WPF app with only 3 pages (for now). I'm using DataTemplate and ContentControl in the main window to display and switch between my pages. (See code sample below). It's working but I have a few concerns:
The DataTemplate use parameterless constructor only. If I add one then It can't find the constructor.
The 'registration' is done in the xaml and I cannot use Dependency Injection to link Views with ViewModels.
Questions:
Is there a way to change that without using third party tool?
If the only good option is to use a tool, which ones should I consider?
<Window.Resources>
<DataTemplate DataType="{x:Type pageViewModels:HomePageViewModel}">
<pageViews:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type pageViewModels:GamePageViewModel}">
<pageViews:GamePageView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
Edit
To clarify, I want to inject a class in the constructor of my viewsModels, but if I do that then the navigation within my application is broken because the dataTemplate is looking for the parameterless constructor.

It looks like my problem has been very well explained in this post.
In short, I need to implement a ViewModelLocator pattern in order to fix all my concerns.

Related

Avoiding ItemTemplate Duplication With WPF Controls?

I have comboboxes that all need to use a converter:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentPresenter
Content="{Binding Converter={StaticResource TimespanConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I'm currently pasting this everywhere I need it, but I am wondering if there is a way to avoid this duplication: to be able to do something like:
<TimeSpanComboBox ...></...> or something similar?
You can define an implicit DataTemplate somewhere in your Application.Resources. i.e.
<DataTemplate DataType="{x:Type sys:TimeSpan}">
<ContentPresenter
Content="{Binding Converter={StaticResource TimespanConverter}}"/>
</DataTemplate>
You can of course also define a key and re-use it explicitly where you need it (e.g. ItemTemplate="{StaticResource TimeSpanTemplate}").

Use Binding value as key for Translation with MarkupExtension

I'm using my own custom localization library that is based on this tutorial with extended features.
Works pretty nifty, but now I want to use it within a ItemControl with a Template for all available languages and get localized values depending on a property of the items.
ViewModel:
public IEnumerable<CultureInfo> AvailableLanguages
{
get
{
return TranslationManager.Instance.AvailableLanguages;
}
}
View:
<ItemsControl ItemsSource="{Binding AvailableLanguages}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type glo:CultureInfo}">
<StackPanel Orientation="Horizontal">
<Image Width="32" Height="24"
Source="{loc:Translate {Binding TwoLetterISOLanguageName}}"/>
<TextBlock Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Obviously using a Binding as Parameter won't work, because of some limitations of MarkupExtensions (you can see the basic implementation in the link above).
Is there a way to work around this issue? Will my only alternative be to do this whole stuff in the code behind?

Changing content according to selected option in WPF

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

WPF, Data Binding, Lookup hierarchy

I am new to WPF, and trying to develop a small application using DataBinding following the MVVM paradigm.
I am trying to provide a binding functionality to the ObservableCollection of Documents (my type). The First Label would display a Documents path (available through Attribute property), while the next children - Pages of the Documents - would display appropriate information: page index and page content (an image).
Here is an issue - how can I create a lookup binding to a parent Label? On Button Click command I would like to pass the documents path, which was available earlier in the first DataTemplate.
Is there a way to tackle this issue?
How would you recommend going around it?
Also, is there a better way to tackle "nested" structures (collections within collections)?
Here is the code https://gist.github.com/b5760982ba81e8ee4036, line 14
you should use RelativeSource:
<ItemsControl ItemsSource="{Binding Documents}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=Attribute.Path}"/>
<ItemsControl ItemsSource="{Binding Pages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Index}"/>
<Button Content="{Binding Content}"
Command="{x:Static viewModel:DocViewModel.Tests }"
CommandParameter="{Binding Path=DataContext.Attribute.Path,RelativeSource={RelativeSource AncestorType=ContentPresenter, Mode=FindAncestor,AncestorLevel=2"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>

WPF Multiple ItemSources?

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.

Categories

Resources