WPF layout created dynamically using MVVM - c#

I'm trying to postion views to the window in layout which is rectangle base while using MVVM pattern.
In WinForms I was able to use width, height, x and y of rectagle to position controls easily by just setting same properties on control.
Now I'm rewriting this code to wpf using MVVM and I'm lost.
this is what I'm trying to do:
This is something I thought might work but it does not.
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding VirtualScreens}" Grid.IsSharedSizeScope="True" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Grid.Row="{Binding Row}" Grid.Column="{Binding Column}" Content="{Binding Name}"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
public class VirtualScreen : ObservableObject
{
string name;
int row;
int column;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged(() => Name);
}
}
public int Row
{
get { return row; }
set
{
row = value;
RaisePropertyChanged(() => Row);
}
}
public int Column
{
get { return column; }
set
{
this.column = value;
RaisePropertyChanged(() => Column);
}
}
}
Thank you for any type of help

you could use ItemsControl ItemsPanel, ItemsTemplate and ItemContainerStyle.
Here is an example for you
<ItemsControl ItemsSource="{Binding VirtualScreens}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Style.Setters>
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Grid.Column" Value="{Binding Column}" />
</Style.Setters>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

You can use a WPF Grid to layout the elements as shown in your UI. A three-row, three-column grid would work fine for you:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2">This is the big top left section</Button>
<Button Grid.Row="1" Grid.Column="2">Top right</Button>
<Button Grid.Row="2" Grid.Column="2">Middle right</Button>
<Button Grid.Row="3" Grid.Column="1">Bottom left</Button>
<Button Grid.Row="3" Grid.Column="2">Bottom center</Button>
<Button Grid.Row="3" Grid.Column="2">Bottom right</Button>
</Grid>
Checkout Grid Row and Column Spanning for more info.

Related

XAML/WPF Binding to Page.Resources - How does binding flow?

I am attempting to use 2 DataTemplates to create something like this below:
I need a control that will allow for my dynamically built name-tags (chips per Material Design in XAML) to populate the blue boxed area stacking horizontally first then stacking vertically as each row fills up. Per my understanding a WrapPanel is what I need to use, I've found various examples and have attempted adapting them to fit my needs. I think I'm dang close with this one, but can't figure out what I'm missing here.
Per my understanding My ScrollViewer.ItemsControl should bring in my x:Key="Recipients" DataTemplate which should implement my x:Key="RecipientChip"DataTemplate for as many instances of Patient are in my IEnumerable<Patient> set called Patients.
I don't think I'm binding things correctly though because when I build, I see nothing printed. It's not throwing any errors in my output tab though so I'm not sure what's going on. My Patients IEnumeral is filled like an indexed object as expected. Can someone help me understand why my chips aren't showing up and how the flow of the bindings go? I feel like I'm passing Patients through my ScrollViewer.ItemsControll to the referenced Controls but maybe I'm just reading into this too much?
Here's my code:
XAML
<Page.Resources>
<DataTemplate x:Key="RecipientChip">
<StackPanel>
<materialDesign:Chip
Content="{Binding Patient.Name}">
<materialDesign:Chip.Icon>
<Image
Source="{Binding Patient.Image}" />
</materialDesign:Chip.Icon>
</materialDesign:Chip>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Recipients">
<StackPanel>
<Label Content="Recipients:" Background="Gray" FontSize="16" />
<ItemsControl x:Name="Recipients" ItemsSource="{Binding Patients}" ItemTemplate="{StaticResource RecipientChip}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32*"/>
<ColumnDefinition Width="126*"/>
<ColumnDefinition Width="Auto" MinWidth="66.999"/>
<ColumnDefinition Width="193*"/>
<ColumnDefinition Width="32*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<materialDesign:Card
Background="{DynamicResource PrimaryHueLightBrush}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
Grid.ColumnSpan="3"
Padding="8"
Margin="0,65,0,50">
<StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemTemplate="{StaticResource Recipients}" />
</ScrollViewer>
</StackPanel>
</materialDesign:Card>
Code Behind:
public partial class EmailMessageView : Page
{
public EmailMessageView(Email email)
{
InitializeComponent();
Email = email;
Patients = email.Patients.Data;
EmailContent.NavigateToString(email.Content);
}
public Email Email;
public IEnumerable<Patient> Patients;
}
Patients
-[0] {Mable} NotificationLinkApp.Models.Patient
Address "9930 Somewhere St. " string
Age 18 double
+ Birthday {NotificationLinkApp.Models.CarbonDate} NotificationLinkApp.Models.CarbonDate
City "Anywhere" string
CommunicationId 3 int?
+ CreatedAt {NotificationLinkApp.Models.CarbonDate} NotificationLinkApp.Models.CarbonDate
Email "Mable.Lang#example.com" string
FirstName "Mable" string
Gender 1 int
HomePhone "555-555-4260" string
HomePhoneIsLandline null int?
Id 1490607 int
Image "pack://application:,,,/Resources/Images/user-unknown.png" string
IsEmail 1 int?
IsMail 1 int?
IsPhone 1 int?
IsReviewRequest 1 int?
IsText 1 int?
LanguageId 25493 int?
LastName "Lang" string
Latitude "40.527834" string
Longitude "-122.318749" string
Name "Mable Lang" string
OfficeId 2 int
ParentId null string
PreferredConfidential "" string
PreferredConfirm "" string
PreferredContact "" string
PreferredRecall "" string
ReferenceId "4634" string
SSN null string
State "New Mexico" string
+ UpdatedAt {NotificationLinkApp.Models.CarbonDate} NotificationLinkApp.Models.CarbonDate
WirelessPhone "555-555-4745" string
WorkPhone "555-555-3226" string
Zip "90134" string
_age 0 double
_email "Mable.Lang#example.com" string
_homePhone "555-555-4260" string
_image "" string
_wirelessPhone "555-555-4745" string
_workPhone "555-555-3226" string
In the end, I was able to get it to work by simplifying the code some as well as setting the DataContext to Patients. I continued to leverage the native functionality of a WrapPanel so that my "chips" would stack horizontally and wrap to a new line much like text. Here is the working code.
XAML
<Page.Resources>
<DataTemplate x:Key="RecipientChip">
<StackPanel>
<materialDesign:Chip
Background="#FFFFFFFF"
Content="{Binding Name}" Click="Chip_Clicked">
<materialDesign:Chip.Icon>
<Image
Source="{Binding Image}" />
</materialDesign:Chip.Icon>
</materialDesign:Chip>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32*"/>
<ColumnDefinition Width="126*"/>
<ColumnDefinition Width="Auto" MinWidth="66.999"/>
<ColumnDefinition Width="193*"/>
<ColumnDefinition Width="32*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<materialDesign:Card
Background="{DynamicResource PrimaryHueLightBrush}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
Grid.ColumnSpan="3"
Padding="8"
Margin="0,65,0,50">
<StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<Label Content="Recipients:" FontSize="16" />
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{StaticResource RecipientChip}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</materialDesign:Card>
<WebBrowser Name="EmailContent" Grid.Row="1" Grid.Column="1" Grid.RowSpan="5" Grid.ColumnSpan="3" Margin="0,52,0,10"/>
</Grid>
Code Behind
public EmailMessageView(Email email)
{
InitializeComponent();
Email = email;
Patients = email.Patients.Data;
EmailContent.NavigateToString(email.Content);
DataContext = Patients;
}
public Email Email;
public IEnumerable<Patient> Patients;

How to bind images to a list where the grid that is nesting the images has a different bindingcontext?

I am currently working with a list that i bind through my viewmodel and then a service.
In the list i bind an image and a label. I can successfully get the label binded, but the image does not show. The reason for this seems to be because i change the bindingcontext in the grid where the image is nested inside. I change the bindingcontext in order to get an accurate width for the image.
If i move the image outside the grid, then it shows, so now i am trying to get the bindingcontext in order again.
What i tried to do with the image was to add a new bindingcontext
BindingContext="{x:Reference Name=gridFollowers}" Source="{Binding image}"
where gridFollowers is what i name my stacklayout where the original binding is made, but unfortunately the image still does not show.
here is the full code:
<StackLayout x:Name="gridFollowers"
BindableLayout.ItemsSource="{Binding headerService.CurrentHeader}">
<BindableLayout.ItemTemplate >
<DataTemplate>
<Grid WidthRequest="75">
<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="7.5*" />
</Grid.ColumnDefinitions>
<Grid
Grid.Row="0"
Grid.Column="0"
BindingContext="{x:Reference Name=headerImage}"
WidthRequest="{Binding Height}">
<ffimageloading:CachedImage
x:Name="headerImage" BindingContext="{x:Reference Name=gridFollowers}"
Source="{Binding image}" />
</Grid>
<Label Grid.Row="1" Grid.Column="0"
Text="{Binding firstname}" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
How can i make it so the image shows?
I try your code and modify some code as following, please replace your code.
<Grid
Grid.Row="0"
Grid.Column="0"
WidthRequest="{Binding Height, Source={x:Reference headerImage}}">
<ffimageloading:CachedImage x:Name="headerImage" Source="{Binding image}" />
</Grid>
Update:
please check headerService.CurrentHeader data.
Please check if binding viewmodel to page.cs bindingcontext by code behind.
<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="7.5*" />
</Grid.ColumnDefinitions>
<Grid
Grid.Row="0"
Grid.Column="0"
WidthRequest="{Binding Height, Source={x:Reference headerImage}}">
<ffimageloading:CachedImage x:Name="headerImage" Source="{Binding image}" />
</Grid>
<Label
Grid.Row="1"
Grid.Column="0"
Text="{Binding firstname}" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
public partial class Page8 : ContentPage
{
public ObservableCollection<imagemodel> models { get; set; }
public Page8 ()
{
InitializeComponent ();
models = new ObservableCollection<imagemodel>()
{
new imagemodel(){firstname="first image", image="a1.jpg"},
new imagemodel(){firstname="second image",image="a2.jpg"}
};
this.BindingContext = this;
}
}
public class imagemodel
{
public string firstname { get; set; }
public string image { get; set; }
}

Binding a Command not Working for ListView with Itemsource

I'm trying to add a Clickable ListView-Cell, but I'm probably getting conflicts with the binding.
Each cell should be like a button navigating to a new Page including a given Object.
I already tried with "TapGestureRecognizer".
<ListView x:Name="ListView" HasUnevenRows="True" SelectionMode="Single" >
<ListView.ItemsSource>
<x:Array Type="{x:Type clients:MinRepresentation}">
<clients:MinRepresentation Id="123456789" PlannedStartTime="01-01-2019" PlannedEndTime="01-12-2019" />
<clients:MinRepresentation Id="555555555" PlannedStartTime="12-12-2019" PlannedEndTime="12-12-2019" />
</x:Array>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OrderDetailsCommand}"/>
</Grid.GestureRecognizers>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
[Cell-Template]
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Got the easy solution...
XAML:
<ListView HasUnevenRows="True" SelectionMode="Single" ItemsSource="{Binding VisibleOrders}" ItemSelected="OnListViewItemSelected" ItemTapped="OnListViewItemTapped"/>
ViewModel:
public IList<MinRepresentation> VisibleOrders { get; private set; }
void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
{
MinRepresentation selectedItem = e.SelectedItem as MinRepresentation;
}
void OnListViewItemTapped(object sender, ItemTappedEventArgs e)
{
MinRepresentation tappedItem = e.Item as MinRepresentation;
}

Bind List containing different elements to Itemscontrol

I am developing a WPF application using MVVM software architecture. In my ViewModel I have a property called SelectedAttributes.
private ObservableCollection<NodeAttributeViewModel> _selectedAttributes;
public ObservableCollection<NodeAttributeViewModel> SelectedAttributes
{
get
{
return _selectedAttributes;
}
set
{
_selectedAttributes = value;
OnPropertyChanged("SelectedAttributes");
}
}
NodeAttributeViewModel is defined as
class NodeAttributeViewModel : ViewModelBase
{
public enum ElementType { BOOL, STRING }
public ElementType Type { get; set; }
public NodeAttribute Attribute { get; set; }
public string AttributeName
{
get
{
return Attribute.Attribute;
}
set
{
Attribute.Attribute = value;
OnPropertyChanged("AttributeName");
}
}
public string AttributeValue
{
get
{
return Attribute.Value;
}
set
{
if (!Attribute.Value.Equals(value))
{
Attribute.Value = value;
OnPropertyChanged("AttributeValue");
}
}
}
}
I want to bind the SelectedAttributes list to a ItemsControl. Depending on the Type of element of list element, I want to display a checkbox(for boolean), TextBox ( for String). Currently I am able display all the elements in a TextBox. I dont know how to display checkbox for elements whose type is Boolean
Here is my Xaml
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Column="2" Margin="9,6,9,6">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=SelectedAttributes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding AttributeName}" />
<TextBox Text="{Binding AttributeValue}" Grid.Column="1" />
</Grid>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Grid.Row="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
here you go
you can make use of DataTrigger to control what type of editor should be displayed for different type of attribute
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding AttributeName}" />
<TextBox x:Name="text"
Text="{Binding AttributeValue}"
Grid.Column="1" />
<CheckBox x:Name="check"
IsChecked="{Binding AttributeValue}"
Grid.Column="1"
Visibility="Collapsed" />
</Grid>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"
Grid.Row="1" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Type}"
Value="BOOL">
<Setter Property="Visibility"
TargetName="check"
Value="Visible" />
<Setter Property="Visibility"
TargetName="text"
Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I have added a CheckBox in the same column as TextBox and set it's Visibility to Collapsed by default
when the value of Type is BOOL the trigger setters will swap the Visibility of CheckBox and TextBox hence making CheckBox appear and TextBox disappear and will also reset it back when value is not BOOL
You can put Trigger on your Checkbox visiblity depending on your Type property
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<CheckBox>
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="BOOL">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<TextBlock Grid.Column="2" Text="{Binding AttributeName}" />
<TextBox Text="{Binding AttributeValue}" Grid.Column="2" />
</Grid>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Grid.Row="1"/>
</Grid>
</DataTemplate>

How to bind dictionary key with the listbox textblock?

I've got a problem get binding working in an DataTemplate of a ListView. My binding target is a KeyValuePair.
this is MY XAML Code:-
<ListBox x:Name="ListBox_Setting" ItemsSource="{Binding DictTemperature}" BorderBrush="Black" BorderThickness="1" Height="582" Width="300">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="78">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="Img_ListSetting" Width="50" Height="50" Source="/Assets/un-checked.png" Margin="18,7,507,21" />
<TextBlock x:Name="Tb_ListSetting" Text="{Binding Path=Key}" FontFamily="OpenSans" FontSize="30" Margin="89,0,18,20" Height="42" VerticalAlignment="Bottom" />
<Rectangle x:Name="Line_ListSetting" Height="1" Margin="15,66,15,11" Fill="#FF3498DB"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And This is My C# Code :-
public Dictionary<string, bool> DictTemperature { set; get; }
DictTemperature = new Dictionary<string, bool>();
if (!appSettings.Contains("AppTemperature"))
{
DictTemperature.Add("Celsius", true);
DictTemperature.Add("Fahrenheit", true);
DictTemperature.Add("Kelvin", true);
DictTemperature.Add("Rankine", true);
appSettings.Add("AppTemperature", DictTemperature);
}
DictTemperature = (Dictionary<string, bool>)appSettings["AppTemperature"];
here listbox is showing count as 4 and showing the key value pairs also but unable to bind key with the textblock.
Please tell me the solution....
You can do it Shown here (Binding a Dictionary's key and value in a listbox with wpf)

Categories