How to use Nested Views in WPF MVVM - c#

Hi I am trying to understand the best practise to use nested views.
I have an 'Attribute' View which is bound to collection of name value pairs in the view model. I need to reuse this at various places in my UI.
I have another 'Condition View' which has a string property and Dictionary(string,string) collection. I tried to create a text box and add the Attribute View control in the XAML
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="{x:Static l:StringResources.ConditionViewLabelText}" />
<TextBox Text="{Binding Type,Mode=TwoWay}" Width="100" />
</StackPanel>
<vw:AttributeView />
</StackPanel>
I want to bind the AttributeView's bound property to the Dictionary(string,string)'s collection property of the parent view model. Whats the best way to do it. I am not able to bind the vw:AttributeView to a ConditionViewModels ?
Can you please let me know the best practise to do this?
-- EDIT Please find my AttributeView (This is the child view's xaml code). The data template is bound to an observable collection on the AttributeViewModel
<StackPanel>
<ItemsControl ItemsSource="{Binding AllAttributes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding Name, Mode=TwoWay}" Margin="5,0,5,0" />
<TextBox Width="100" Text="{Binding Value, Mode=TwoWay}" Margin="5,0,5,0" />
<TextBlock Width="100" Text="{Binding KeyValue, Mode=OneWay}" />
<Button Width="50" Content="{x:Static l:StringResources.AttributeViewButtonDeleteText}" Command="{Binding Path=DataContext.DeleteAttribute, ElementName=AttributeControl}" CommandParameter="{Binding Name}"></Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Name="btnSomething" Content="{x:Static l:StringResources.AttributeViewButtonAddText}" Command="{Binding AddNewAttribute}" />
</StackPanel>
</Grid>

Based on your comments that your parent / child relationship is not reflected in your view model, you have two options:
Create the relationship in your view model to reflect the relationship in your view, allowing you to navigate from the parent to the child and vice-versa should you need this.
Use a RelativeSource FindAncestor binding to navigate up the visual tree and locate a control bound to the parent view model, biding to this controls DataContext.
Option (2) will make you look clever, but option (1) is much simpler!

Related

How to have multiple datatemplates in UWP ListView (x:bind)

I'm stuck with my UWP app, I'm trying to follow MVVM principle with {x:bind} instead of the older WPF way {binding name}
But I'm stuck with my idea/development, I'm trying insert multiple DataTemplates within ListView (or GridView).
I want to do something like this as an example:
<ListView.ItemTemplate>
<DataTemplate x:DataType="localModels:Cars">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<TextBlock Text="Name:" />
<TextBox Text="{x:Bind Name}" />
</StackPanel>
</StackPanel>
</DataTemplate>
<!-- Here is the second DataTemplate which isn't allowed in UWP -->
<DataTemplate x:DataType="localModels:Bikes">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<TextBlock Text="Name:" />
<TextBox Text="{x:Bind Name}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
As you can see, I have 2 DataTemplate: Cars and Bikes inside ListView.ItemTemplate.
Not sure if this is relevant: Cars.cs is inheriting from Bike.cs parent.
I don't really get why UWP doesn't accept more than 1 DataTemplate within ListView.ItemTemplate. Because I really want my ListView to show as many kind of data as possible.
What's the way to solve this issue in UWP and {x:bind}?
You can use a DataTemplateSelector to determine which datatemplate should be selected based on your data type. Here you can find an example.

how to bind a command from DataContext of Page to a list item

I have a Page and a viewmodel is set as its datacontext. in that page I have a list. which is populating through a property in the viewmodel. List has a user control. and that user control has a button. I want that button to be bind with a command that is in viewmodel. Is there anyway to do that?
<Page DataContext=PageViewModel>
...
<ScrollViewer Grid.Row="3" Margin="20,0" Visibility="{Binding ByVenueSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<ItemsControl ItemsSource="{Binding EventsListByVenue}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myControls:EventDetails /> <!--in this control i want to bind a command available in PageViewModel-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
...
</Page>
with help of #FunksMaName, I solved this. I am sure there are more elegant and better approach, but yet this is a quick & easy solution for me:
<Button Style="{StaticResource NoHighlightButtonStyle}" Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Visibility="{Binding link,Converter={StaticResource DataAvailabilityToVisibilityConverter}}" Command="{Binding Path=Event.LinkCommand,Source={StaticResource Locator}}" >
<TextBlock Margin="0,5" Foreground="SkyBlue" Text="{Binding link}" TextWrapping="Wrap" FontSize="16"/>
</Button>
things to note:
i think xaml searched the command parameter in context of command only, so it was giving me null parameter, while the same binding was working fine for textblock inside Button. So i tricked it to store the value in tag, and used it from there.
Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}"

Binding data to ListView doesn't work (Metro UI)

When I put this code into my XAML file:
<TextBlock Text="{Binding Name}" FontSize="16" Margin="15,0,0,0" />
It shows me Name value but when I try show it via ListView:
<ListView x:Name="ItemListView"
ItemsSource="{Binding Items}"
Margin="60,0,0,10">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"
FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
<TextBlock Text="{Binding Artist}"
FontSize="16" Margin="15,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It doesn't show up :( What could be wrong?
If the page inherits from LayoutAwarePage, like it will by default when created from a template, the DataContext will be set to a property named DefaultViewModel. Properties exposed in your codebehind will not be bound to.
You are looking at two different objects in the sample. In the textbox you are looking at the Name of the object in the datacontext, in the listview you are looking at the Items[x].Name for each item you bind to. If you need to reference the Name from the datacontext rather than the Items collection you will need to bind the source to an Element not inherit it.

Problem when setting up command bindings in XAML

I am trying to create a menu bar for my application. I have created a collection in xaml that I will contain menu items that my menu will bind to.
In xaml I have created an array that I use as my static resource for binding.
<coll:ArrayList x:Key="MenuOptionsList">
<model:DashboardMenuBarItem
Icon="the location of an image in my images folder"
DisplayName="The test that will appear under my button"
CommandName="someCommandInMyViewModel"/>
</coll:ArrayList>
I am using a listbox with a data template to show these items as follows.
<ListBox x:Name="lstNavigateTo" MinWidth="400" DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource MenuOptionsList}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource horizontalListTemplate}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<Button Height="60" Width="60"
Command="{Binding Mode=OneWay, Path=CommandName}">
<Button.Content>
<Image Source="{Binding Path=Icon}" Grid.Row="0" />
</Button.Content>
</Button>
<TextBlock Text="{Binding Path=DisplayName}"
Width="100" TextAlignment="Center" Grid.Row="1" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My problem is that I am using the MVVM design pattern and cannot get the command bindings to work on the button click. Previously I would have managed the button click like this.
Command="{Binding someCommandInMyViewModel}"
That would work fine but when I try to bind a command to a property of an item in my collection the command will not fire.
Does anyone know how I can achieve this.
The CommandName property in your collection is of type String, whereas the Command property on Button is of type ICommand. In what way are you expecting WPF to resolve an ICommand from a String? You'll need to help it: either create a converter and use it in your binding, or change your CommandName property so that it contains an actual ICommand rather than a String.

Get element inside a DataTemplate

I have a following XAML and I need to change visibility of imageRemoveButton at runtime from the code behind file. How do I access that button?
<ItemsControl x:Name="ImagesItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#ffdddddd"
BorderThickness="0,0,0,1">
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1"
VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap" />
</Grid>
<Button Grid.Column="3"
Width="20"
Height="20"
Content="X"
Template="{StaticResource ButtonAddTab}"
Style="{StaticResource ButtonWizard}"
Tag="{Binding}"
x:Name="imageRemoveButton"
Click="ImageRemoveButton_Click" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use the loaded event on the button to save a reference to the button in the cs file. Use this reference when needed to change the visibility.
You cannot reference-by-name elements in a template. There is no matching code-behind/designer property generated by templates.
You want to bind the visibility of a control in the template to a property of your data items. That means you will need to add a property to your data items. Can you provide more details of your data items? If the property is a bool value, use a VisibilityConvertor (dime a dozen on this site).
Think of templates as wanting to always pull settings from the bindings, rather than have settings stuffed into them from the outside.
Try using Source binding to a class that will manage the button's visibility state. Adding a property to all your data items would be painfull but could be required if the button's visibility needs to be set based on a property of your data item. As per standard MVVM patterns you can then control the button's visibility from C#. If you need a specific instance of the button you can add a Loaded event handler to it and cache it accordingly.

Categories