WPF MVVM Binding dynamic control in code behind and pass in View - c#

I am working on WPF application using MVVM. I have two page. I have multiple UserControls in a page 1, on selection of UserControls from page 1, I want to show that selected userControl in 2nd page. Below are my code.
ViewModel Code
public RelayCommand<string> OnClickSelectWidgetCommand => new RelayCommand<string>((setUserControlName) =>
{
using (new CursorWait())
{
var MyContentControl = setUserControlName;
MessageBox.Show(MyContentControl);
//How to render UserControl to View?
}
}, true);
Here in above code I get the UserControl name in setUserControlName variable. Now how to bind that UserControl to XAML page? Below are my code that I have tried.
View Code
<StackPanel Background="Black" VerticalAlignment="Top">
<Border Name="UserControl1BorderLow" BorderBrush="White" BorderThickness="0" >
<ItemsControl ItemsSource="{Binding LowCollection}" Margin="4,0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" />
<!--what can I do here in above line to make it dynamically render the userControl in place of UserControlColumn1XL-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border></StackPanel>
Above code, In DataTemplate what need to be change to bind UserControls dynamically?

There are two ways to solve this, one involves setting the template based on your data type (DataTemplates) and the second involves setting it based on the data itself (DataTriggers).
In the first case your LowCollection should be an array of objects, or some base class that your view models are all derived from (ViewModel1, ViewModel2 etc). In this case you can get rid of your itemtemplate altogether and just add DataTemplates to specify how each of the items in your ItemsControl should be represented:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<UserControl2 />
</DataTemplate>
... etc...
In the second case you need to set a template based on the value of some property in your view model. In this case you do need to set the ItemTemplate, and you give it a Style which uses data triggers to set an appropriate DataTemplate:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue1">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue2">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
The relevant parts to note here are that there is a property in your view model called YourProperty which can have two values i.e. YourValue1 or YourValue2; the style above then selects either YourDataTemplate1 or YourDataTemplate2, depending on the value of YourProperty.

Related

In WPF is the contents of ListBox the same as its bound ItemsSource? and how do I seperate them?

I have a ListBox with its ItemsSource bound to some UserControl-s and shown based on their current states(IsVisible)
Here is the code
<ListBox x:Name="sidebarList" Margin="0,0,10,10" ItemsSource="{Binding Modules, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0 5 0 5" Visibility="{Binding IsVisible, Mode=TwoWay}">
<TextBlock Text="{Binding Title}" FontWeight="Bold" />
<TextBlock Margin="0 5 0 0" MaxWidth="200" Foreground="Gray" Text="{Binding Detail}"
TextWrapping="WrapWithOverflow">
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="Collapsed">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
And now I'm trying to only get "IsVisible"/selected parts when a button is pressed. As of right now, the ListBox actually contains every option but just showing the IsVisible ones(at least that's what I think).
How do I only get the selected options? I have tried to use a new List with the same type as the UserControl-s type(which I placed in app.xaml.cs to call from anywhere) and add to that list when their state changes to IsVisible
but the List doesn't show up in my classes where I check them.
Sounds like what you really need is a view-model. Bind your Listbox to a view model that has an observable collection that only shows view-models of the items you want to present.
I can give you a more "correct" answer about how to do it with style/data-triggers but you should really just learn about MVVM and use a view-model to simplify.

How to add a WPF Button to add content to a List inside an ObservableCollection?

From a Database I am receiving a List with multiple Properties. One of those Properties is again a List of an Appendix Object containing ID Information and an Image.
I converted that List into an ObservableCollection and bound it to a WPF ListBox. Using a DataTemplate I show this content. Inside the DataTemplate I now have an ItemControl bound to the Inner-List (my Appendix with none, one or multiple entries):
<ListBox ItemsSource="{Binding Reports}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="Report Date:" FontWeight="Bold"/>
<Label Content="{Binding ReportDate.Date, StringFormat=dd.MM.yyyy , ConverterCulture=de-DE}"/>
<Label Content="Comment:" FontWeight="Bold"/>
<Label Content="{Binding Comment}"/>
</StackPanel>
//Here I would like to have a Button to add a Picture into the Appendix List
<ItemsControl x:Name="SelectedOnlyListBox" ItemsSource="{Binding Appendix}" Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Picture}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}, Path=IsSelected}" Value="True">
<Setter Property="Visibility" Value="Visible" TargetName="SelectedOnlyListBox" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
My question is, how do I get the current Object via Binding into my Code. Because I will have multiple Buttons for one outer List (Reports) entry. But the Button should only add into the proper Inner List (Appendix).
Additional info: I am using the MVVM-Light Toolkit.
Thank you very much.
Edit: I forgot to mention that the first List is one of many Properties of a Detail class which again comes in a List of all Details. So with my DataContext already got a Property "SelectedDetail". This one I know...
But how do I now match the specific Button of each SelectedDetail.Report to my SelectedDetail.Reports.Appendix?

Conditional Static Resource For Itemsource

at the moment in order to fix a bug from telerik, my ItemsSource must be pointing to the viewmodel I'm currently working with.
Relationship.xaml
<UserControl.Resources>
<Client:PersonViewModel x:Key="MyViewModel"/>
</UserControl.Resources>
Where it's used.
<Telerik:GridViewComboBoxColumn Header="Relationship"
ItemsSource="{Binding GridRelationshipTypes, Mode=TwoWay, Source={StaticResource MyViewModel}}"
DataMemberBinding="{Binding RelationshipType}"
SelectedValueMemberPath="Id"
DisplayMemberPath="Name"
IsReadOnly="False"/>
I have four other view models this logic needs to be applied to. I don't want to create 5 different UserControls for such a small thing. I'm wondering if I can create a method such that it'll check what the current viewmodel type is and will use the corresponding viewmodel.
PseudoCode - ViewModelTypes is an enum.
public void StaticResourcToUse(ViewModelTypes viewModelType)
{
if (viewModelType == ViewModelTypes.PersonViewModel)
use personviewmodel resources
if (viewModelType == ViewModelTypes.BusinessViewModel)
use businessViewModel resources
}
If I understand correctly what you want is switch your view based on view model.
Use a ContentControl to display the data, and swap out the ContentTemplate in a trigger based on the property that changes.
Here's an example in Rachel Lim's blog that swaps a template based on a bound property:
<DataTemplate x:Key="CarTemplate" TargetType="{x:Type local:YourViewModel}">
<TextBlock Text="I'm a Car" />
</DataTemplate>
<DataTemplate x:Key="TrackTemplate" TargetType="{x:Type local:YourViewModel}">
<TextBlock Text="I'm a Track" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:YourViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource CarTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourType}" Value="Track">
<Setter Property="ContentTemplate" Value="{StaticResource TrackTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>

WPF TemplateBinding to DataContext of templated parent

We have four identical popups with grids in four XAML views. I'd like to move that XAML to a template and apply via a Style to to ContentControls in all four of them. The trouble is passing in the source of the items in the grids. We get that from each of four different view models. It's different in each case, the only thing that differs among the four cases. I'll probably end up renaming them consistently, but I'd like to think that's a separate issue.
Obviously I don't understand TemplateBinding at all. How do I bind a property of a child of the template to a property of the ContentControl that I'm applying the template to?
Except for the value of the DataSource attribute changing, the XAML for the grid is identical to what works perfectly well when we use it directly.
I added the TextBlock just to see if I could bind anything at all. I do get NaN there.
<Style x:Key="HistoryPopupContentStyle" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{TemplateBinding Width,
diag:PresentationTraceSources.TraceLevel=High}"
Background="White"
Foreground="Black"/>
<dxg:GridControl
DataSource="{Binding RelativeSource={RelativeSource
Path=DataContext,
TraceLevel=High}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
>
<!-- Columns. The grid displays column headers
as desired but with no rows -->
</dxg:GridControl.Columns>
</dxg:GridControl>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Popup
Name="PopHistory"
DataContext="{Binding Path=HistoryList}"
>
<ContentControl DataContext="{Binding Path=HistoryList}"
Style="{StaticResource HistoryPopupContentStyle}"
Name="Testing"
/>
</Popup>
You will need to subclass ContentControl (or just Control), so that you can add new dependency properties.
public class GridControl : ContentControl
{
// TODO add dependency properties
public GridControl()
{
DefaultStyleKey = typeof(GridControl);
}
}
Add your "Items" dependency property to the above control (type IEnumerable).
Next, update your template to target the new type:
<Style x:Key="HistoryPopupContentStyle" TargetType="local:GridControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<dxg:GridControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:GridControl},Path=Items}" />
Alternately, you could set the "Template" instead of the "ContentTemplate". This would be when you use TemplateBinding:
<Style x:Key="HistoryPopupContentStyle" TargetType="local:GridControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:GridControl">
<dxg:GridControl ItemsSource="{TemplateBinding Items}" />
Use it by binding the Items property to your source items:
<local:GridControl Style="{StaticResource HistoryPopupContentStyle}"
Items="{Binding Path=HistoryList}" />
You could also skip creating a subclass altogether, and just use the Content property of ContentControl to stash the items:
<Style x:Key="HistoryPopupContentStyle" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<dxg:GridControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=local:GridControl},Path=Content}" />
Or using the Template / TemplateBinding approach
<Style x:Key="HistoryPopupContentStyle" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<dxg:GridControl ItemsSource="{TemplateBinding Content}" />
Use like this:
<ContentControl Style="{StaticResource HistoryPopupContentStyle}"
Content="{Binding Path=HistoryList}" />

wpf combobox with custom itemtemplate text

I have ComboBox with custom ItemTemplate.
<ComboBox Height="20" Width="200"
SelectedItem="{Binding Path=SelectedDesign}"
ItemsSource="{Binding Path=Designs}" HorizontalAlignment="Left"
ScrollViewer.CanContentScroll="False">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type formdesign:FormDesignContainer}">
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding Path=ImageThumb}" Stretch="Uniform" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This works well. However WPF tries to draw rectangle as Combobox Text. How can I set "text" for this template. By "text" I mean string or control which represent selected item and write into combobox when item is selected
In other words I'd like to do this:
But now I got this
Try setting SelectionBoxItemTemplate with a TextBlock.
Appears that SelectionBoxItemTemplate is read-only. So another approach is to override ItemContainerStyle.Template. Example
I found this solution by Ray Burns a good approach. You can define two DataTemplate one for Items in the drop down list and the other for the selected item which should be shown in the Combobox. The using a trigger and checking the visual tree it decide which one to use.
<Window.Resources>
<DataTemplate x:Key="NormalItemTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
...
<ComboBox
ItemTemplate="{StaticResource CombinedTemplate}"
ItemsSource="..."/>
Add Textblock to the datatemplate and bind it
or add Contentpersenter on the rectangle
Edit:
it seems like i didn't got what you were tring to accomplish ,

Categories

Resources