I am trying to display an object in a grid using data binding. For some reason Only one Row of the grid is being displayed per entry. I know the data binding works, because if I comment out the first row of each grid it then displays the second row. Below is a sample of the relevant code, as well as a screenshot displaying the issue.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Game_Tracker.ViewModels;assembly=Game Tracker"
x:Class="Game_Tracker.MainPage">
<ContentPage.BindingContext>
<local:OpponentItemVm/>
</ContentPage.BindingContext>
<StackLayout>
<ListView x:Name="OpponentList" ItemsSource="{Binding OpponentItems }">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BorderColor="Black"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Padding="0">
<Grid>
<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"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name: "/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OpponentFullName}"/>
<Label Grid.Row="1" Grid.Column="0" Text="Email: "/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding OpponentEmail}"/>
<Label Grid.Row="2" Grid.Column="0" Text="Phone: "/>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding OpponentPhone}"/>
<Label Grid.Row="3" Grid.Column="0" Text="Address: "/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding OpponentAddress}"/>
<Label Grid.Row="4" Grid.Column="0" Text="Id: "/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding OpponentId}"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Game_Tracker
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
}
Opponent.cs
using System;
namespace Game_Tracker.Models
{
public class Opponent
{
public int OpponentId { get; set; }
public string OpponentFName { get; set; }
public string OpponentLName { get; set; }
public string OpponentAddress { get; set; }
public string OpponentPhone { get; set; }
public string OpponentEmail { get; set; }
public string OpponentFullName => $"{OpponentFName} {OpponentLName}";
public Opponent(int opponentId, string opponentFName, string opponentLName,
string opponentAddress, string opponentPhone, string opponentEmail)
{
this.OpponentId = opponentId;
this.OpponentFName = opponentFName;
this.OpponentLName = opponentLName;
this.OpponentAddress = opponentAddress;
this.OpponentPhone = opponentPhone;
this.OpponentEmail = opponentEmail;
}
}
}
OpponentItemVM.cs
using System.Collections.ObjectModel;
using Game_Tracker.Models;
namespace Game_Tracker.ViewModels
{
public class OpponentItemVm
{
public ObservableCollection<Opponent> OpponentItems { get; set; }
public OpponentItemVm()
{
OpponentItems = new ObservableCollection<Opponent>();
OpponentItems.Add(new Opponent(1, "Jim", "Jimson",
"123 Fake Street", "(123)123-4567", "Jim#Fake.com" ));
OpponentItems.Add(new Opponent(2, "Ken", "Kensington",
"123 False Street", "(123)123-4567", "Ken#Fake.com" ));
}
}
}
To be honest, I agree with Jason.However, the best practice is that you just add HasUnevenRows to True and let unset the RowHeight property which will automatically set the row height to fit content like below:
<ListView x:Name="OpponentList" ItemsSource="{Binding OpponentItems }" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BorderColor="Black"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Padding="0"
>
<Grid>
<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"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="Name: "/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding OpponentFullName}"/>
<Label Grid.Row="1" Grid.Column="0" Text="Email: "/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding OpponentEmail}"/>
<Label Grid.Row="2" Grid.Column="0" Text="Phone: "/>
<Label Grid.Row="2" Grid.Column="1" Text="{Binding OpponentPhone}"/>
<Label Grid.Row="3" Grid.Column="0" Text="Address: "/>
<Label Grid.Row="3" Grid.Column="1" Text="{Binding OpponentAddress}"/>
<Label Grid.Row="4" Grid.Column="0" Text="Id: "/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding OpponentId}"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Official reference docs:
https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.listview.hasunevenrows?view=xamarin-forms
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#row-height
Related
Firstscreen with all the notes.
After I deleted the first note!
I have recently started programming with .NET MAUI. The elements are correctly removed in the C# list. However, after deleting the remaining elements are only partially displayed. That means that only e.g. the 4th element is displayed. For the other elements only an empty bar is displayed.
My code so far:
XAML:
<VerticalStackLayout>
<ScrollView>
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="logo.png"
WidthRequest="150"
HorizontalOptions="Start"
VerticalOptions="Start"/>
<Label
TextColor="Black"
Grid.Column="1"
Text="TODO"
FontSize="35"
HorizontalOptions="Start"
VerticalOptions="Start"
Margin="23"/>
</Grid>
<Grid BackgroundColor="#24D4A3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView
Grid.ColumnSpan="2"
Grid.RowSpan="2"
RowHeight="100"
x:Name="listview">
<ListView.ItemTemplate>
<DataTemplate >
<ViewCell >
<Grid BackgroundColor="#24D4A3">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button BackgroundColor="#DEABF5"
Text="{Binding Title}"
Clicked="onNoteSelected"
BorderWidth="2"
TextColor="Black"
FontSize="28"
Margin="20"
CornerRadius="100"
WidthRequest="350"
HeightRequest="70"
HorizontalOptions="Center"
VerticalOptions="Start"/>
<Button
BindingContext="{Binding Id}"
Clicked="ToDoSolved"
BorderWidth="2"
BorderColor="Black"
BackgroundColor="White"
WidthRequest="45"
HeightRequest="45"
CornerRadius="35"
Margin="0,0,260,0"
/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ImageButton
Clicked="Settings"
Source="settings.png"
Grid.Row="0"
Grid.Column="2"
BorderColor="#2b3c3c"
BorderWidth="0"
BackgroundColor="#34A4EB"
CornerRadius="35"
HorizontalOptions="End"
WidthRequest="70"
HeightRequest="70"
Margin="0,10, 10, 0"
VerticalOptions="Start"/>
<ImageButton
Clicked="CreateNote"
Source="add.png"
Grid.Row="1"
Grid.Column="2"
BorderColor="#2b3c3c"
BorderWidth="0"
BackgroundColor="#34A4EB"
CornerRadius="35"
HorizontalOptions="End"
WidthRequest="70"
HeightRequest="70"
Margin="0,0,10,10"
Padding="2,0,0,0"/>
</Grid>
</StackLayout>
</ScrollView>
</VerticalStackLayout>
C#:
public partial class MainPage : ContentPage
{
private ObservableCollection<Note> notes = new ObservableCollection<Note>();
public ObservableCollection<Note> Notes
{
get { return notes; }
set { notes = value; }
}
public MainPage()
{
InitializeComponent();
notes.Add(new Note(1, "My Note1", "I'm ugly"));
notes.Add(new Note(2, "My Note2", "I'm short"));
notes.Add(new Note(3, "My Note3", "I'm smart"));
notes.Add(new Note(4, "My Note4", "I'm smart"));
//notes.Add(new Note(6, "My Note6", "I'm smart"));
//notes.Add(new Note(7, "My Note7", "I'm smart"));
//notes.Add(new Note(8, "My Note8", "I'm smart"));
//notes.Add(new Note(9, "My Note9", "I'm smart"));
this.BindingContext= Notes;
listview.ItemsSource= Notes;
}
private async void CreateNote(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//CreateNote");
}
private async void Settings(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//Settings");
}
private void ToDoSolved(object sender, EventArgs e)
{
Button button= (Button) sender ;
var id = (int)button.BindingContext;
var item = Notes.SingleOrDefault(x => x.Id == id);
if (item != null)
{
Notes.Remove(item);
Console.WriteLine(id);
}
}
async void onNoteSelected(object sender, EventArgs e)
{
Button button= (Button) sender ;
var id = (int)button.BindingContext;
//await Shell.Current.GoToAsync("NotePage" + id);
}
}
I would be grateful for any help :)
To delete the item, since you are using ListView, you can delete the via
Button click. However, you need to delete the item from bottom to top in order.
Code-behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Note> Notes { get; private set; } = new ObservableCollection<Note>();
public MainPage()
{
InitializeComponent();
AddNotes();
BindingContext = this;
}
private void AddNotes()
{
Notes.Add(new Note("0", "My Note1"));
Notes.Add(new Note("1", "My Note2"));
Notes.Add(new Note("2", "My Note3"));
Notes.Add(new Note("3", "My Note4"));
}
private void Button_Clicked(object sender, EventArgs e)
{
var note = (Button)sender;
Note listnote = (from itm in Notes
where itm.Id == note.CommandParameter.ToString()
select itm).FirstOrDefault<Note>();
Notes.Remove(listnote);
}
}
Xaml:
<VerticalStackLayout>
<ScrollView>
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="logo.png"
WidthRequest="150"
HorizontalOptions="Start"
VerticalOptions="Start"
/>
<Label
TextColor="Black"
Grid.Column="1"
Text="TODO"
FontSize="35"
HorizontalOptions="Start"
VerticalOptions="Start"
Margin="23"
/>
</Grid>
<Grid BackgroundColor="#24D4A3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView
Grid.ColumnSpan="2"
Grid.RowSpan="2"
RowHeight="100"
x:Name="listview"
ItemsSource="{Binding Notes}" >
<ListView.ItemTemplate>
<DataTemplate >
<ViewCell>
<Grid BackgroundColor="#24D4A3" >
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button BackgroundColor="#DEABF5"
Text="{Binding Title}"
BorderWidth="2"
TextColor="Black"
FontSize="28"
Margin="20"
CornerRadius="100"
WidthRequest="350"
HeightRequest="70"
HorizontalOptions="Center"
VerticalOptions="Start"/>
<Button
Text="Delete"
Clicked="Button_Clicked"
CommandParameter="{Binding Id}"
BorderWidth="2"
BorderColor="Black"
BackgroundColor="White"
WidthRequest="45"
HeightRequest="45"
CornerRadius="35"
Margin="0,0,260,0"
/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</StackLayout>
</ScrollView>
</VerticalStackLayout>
Note:
public class Note
{
public string Id { get; set; }
public string Title { get; set; }
public Note(string id, string title)
{
Id = id;
Title = title;
}
}
Hey so I've set up a CarouselView with a video in each cell
<CarouselView
x:Name="TheCarousel">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout
HorizontalOptions="Center">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="6*"/>
</Grid.RowDefinitions>
<Label FontSize="Title" HorizontalOptions="Center" HorizontalTextAlignment="Center" Text="{Binding Name}" VerticalOptions="Center" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="1">
<video:VideoPlayer Source="{Binding Uri}" HeightRequest="480"/>
</StackLayout>
</Grid>
</Grid>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
It is working, the videos play, however when i scroll to a different video, the previous one plays by itself. For example, if i pause the first video, then scroll over to the second video, the first video will start playing. I'm not sure why this is happening.
Any help would be appreciated.
Don't know which videoplayer you use, but I use Octane.Xamarin.Forms.VideoPlayer, don't have this problem.
<CarouselView x:Name="TheCarousel" ItemsSource="{Binding videos}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="Center">
<Label
FontSize="Title"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
Text="{Binding title}"
VerticalOptions="Center" />
<o:VideoPlayer
AutoPlay="True"
DisplayControls="True"
FillMode="ResizeAspectFill"
Source="{Binding path}" />
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
public partial class Page5 : ContentPage
{
public ObservableCollection<videomodel> videos { get; set; }
public Page5()
{
InitializeComponent();
videos = new ObservableCollection<videomodel>()
{
new videomodel(){path="https://sec.ch9.ms/ch9/a7e9/abda3d53-bed3-4f20-9f53-70047a35a7e9/Xamarin101WhatIsXamarin_high.mp4",title="title 2"},
new videomodel(){path="https://ia800201.us.archive.org/12/items/BigBuckBunny_328/BigBuckBunny_512kb.mp4",title="title 1"},
new videomodel(){path="https://sec.ch9.ms/ch9/3901/c6e0e4e6-bb93-4033-a484-040a874f3901/Xamarin101XamarinFormsMVVMXAML_high.mp4",title="title 3"}
};
this.BindingContext = this;
}
}
public class videomodel
{
public string path { get; set; }
public string title { get; set; }
}
}
In a CollectionView I have 4 rows, 2 of which are set to "Auto".
In one I have a Label in Binding, in the other I have another CollectionView with objects in Binding. In the Row where I have the Label, the height is automatically set correctly, while where I have the CollectionView it becomes an "infinite" height, in the sense that even if empty, it takes up the whole screen.
<DataTemplate>
<yummy:PancakeView CornerRadius="5" BackgroundColor="Transparent">
<Grid BackgroundColor="Gainsboro" RowSpacing="0.2">
<Grid.RowDefinitions>
<RowDefinition Height="21"/>
<RowDefinition Height="16"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<BoxView Grid.Column="0" Grid.RowSpan="4" BackgroundColor="{Binding ColoreUmore}"/>
<Label Grid.Row="0" Grid.Column="2" Text="{Binding GiornoTrascritto}" FontSize="14" TextColr="Gray" Margin="0,5,0,0"/>
<Label Grid.Row="1" Grid.Column="2" Text="{Binding Orario}" FontSize="14" TextColr="Gray"/>
<Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="1" Margin="8" Source="{Binding Umore}" VerticalOptions="Start" HeightRequest="45"/>
<Label Grid.Row="3" Grid.Column="2" TextColor="Black" FontSize="16" Text="{Binding Nota}" Margin="0,0,10,0" VerticalOptions="Start" HorizontalTextAlignment="Start" VerticalTextAlignment="Start"/>
<CollectionView
Grid.Row="2"
Margin="0,0,10,0"
Grid.Column="2"
SelectionMode="None"
ItemsSource="{Binding IconDiaries}"
VerticalOptions="Start">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" VerticalItemSpacing="5" HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0.5">
<Grid.RowDefinitions>
<RowDefinition Height="17"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="Gray" Grid.ColumnSpan="2" Grid.Row="0" CornerRadius="5" Opacity="0.6" />
<Image Grid.Row="0" Grid.Column="0" Source="{Binding isSource}" Margin="3"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding id}" TextColor="Black" FontSize="12" VerticalTextAlignment="Center" Margin="0,0,3,0"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</yummy:PancakeView>
</DataTemplate>
while where I have the CollectionView it becomes an "infinite" height, in the sense that even if empty, it takes up the whole screen.
From your code and description, you have one collectionview in datatemplate, now you want to adjust collectionview height according to its content, am I right?
If yes, I suggest you can binding collectionview HeightRequest like the following code:
<StackLayout >
<CollectionView
x:Name="assessmentsCollectionView"
BackgroundColor="Blue"
HeightRequest="{Binding rowHeigth}"
ItemsSource="{Binding letters}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="5" Spacing="10">
<Frame
Padding="0"
CornerRadius="5"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal">
<Frame
Padding="5"
CornerRadius="0"
WidthRequest="50">
<Label
FontSize="24"
HorizontalTextAlignment="Center"
Text="{Binding TypeLetter}"
TextColor="#37474f"
VerticalTextAlignment="Center" />
</Frame>
<StackLayout Padding="10">
<Label
FontSize="24"
Text="{Binding Name}"
TextColor="Black" />
<StackLayout Orientation="Horizontal">
<Image
HeightRequest="12"
Source="c1.png"
WidthRequest="12" />
<Label
FontSize="12"
Text="{Binding Date}"
TextColor="Gray" />
</StackLayout>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button
x:Name="btn1"
Command="{Binding AddCommand}"
Text="btn1" />
</StackLayout>
public partial class Page4 : ContentPage
{
public Page4()
{
InitializeComponent();
this.BindingContext = new letterviewmodel(); ;
}
}
public class letterviewmodel: INotifyPropertyChanged
{
public ObservableCollection<lettermodel> letters { get; set; }
private int _rowHeigth;
public int rowHeigth
{
get { return _rowHeigth; }
set
{
_rowHeigth = value;
RaisePropertyChanged("rowHeigth");
}
}
public ICommand AddCommand { protected set; get; }
public letterviewmodel()
{
letters = new ObservableCollection<lettermodel>()
{
new lettermodel(){TypeLetter="A",Name="letter 1",Date="2021-01-01"},
new lettermodel(){TypeLetter="B",Name="letter 2",Date="2021-01-01"},
new lettermodel(){TypeLetter="C",Name="letter 3",Date="2021-01-01"}
};
rowHeigth = letters.Count * 100 ;
AddCommand = new Command<lettermodel>(async (key) => {
letters.Add(new lettermodel() { TypeLetter = "D", Name = "test letter ", Date = "2021-01-01" });
rowHeigth = letters.Count * 100 ;
});
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class lettermodel
{
public string TypeLetter { get; set; }
public string Name { get; set; }
public string Date { get; set; }
}
The screenshot:
If I understand correctly, you want the CollectionView to take an automatic height and adapt to the row, right?
In order to make this happen you will need to:
Set the property
ItemSizingStrategy="MeasureFirstItem" in the CollectionView, so the CollectionView has the proper height you're looking for.
Set the Row Height to *, so the CollectionView height sets automatically for the remaining size of the page.
Set VerticalOptions to FillAndExpand in Collection View so the CollectionView can adapt to the size of the Row.
Finally, put the label with Row 3 below the CollectionView to avoid any issues with the Layout order.
Let me know if this does any help!
I'm learning WPF, I want to pass data from some textboxes to code:
<Grid Margin="0,0,10,43" Name="dataFrm">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0">Name</Label>
<Label Grid.Column="0" Grid.Row="1">Code</Label>
<Label Grid.Column="0" Grid.Row="2">Categry Id</Label>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtName" Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtCode" Text="{Binding Path=Code, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtCatId" Text="{Binding Path=CategoryId, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"/>
<Button Grid.Row="3" Grid.Column="1" x:Name="btnSave" Click="btnSave_Click" Width="142" Content="Save"/>
</Grid>
When I click save, I want to cast the retrieved data to a predefined class:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
var productVm = (ClsProducts)dataFrm.DataContext;
MessageBox.Show(productVm.Name);
}
ClsProducts:
public class ClsProducts
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int CategoryId { get; set; }
}
but the DataContext of dataFrm is null!
Make sure that you set the DataContext property of the Grid or its parent window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ClsProducts();
}
}
I was able to retrieve selected item value from my treeview. Now I want to change the content in ScrollViewer control based on that value. I'm trying to display property values of Facility object which contains Containments which contains Tanks.
I'm new to programming sorry if my explanation is not clear enough.
Thank you.
MyWindow.xaml
<Window.DataContext>
<local:VisioFacilityExportViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="FacilityTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Facility ID:"/>
<TextBox Grid.Row="0" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].FacilityID}"/>
<Label Content="Address:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Address}"/>
<Label Content="Description:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Description}"/>
<Label Content="Layout And Drainage:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].LayoutAndDrainage}"/>
<Label Content="Latitude:" Grid.Row="4"/>
<TextBox Grid.Row="4" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Latitude}"/>
<Label Content="Longitude:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Longitude}"/>
<Label Content="City:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].City.Name}"/>
<Label Content="County:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].County.Name}"/>
<Label Content="Name:" Grid.Row="8"/>
<TextBox Grid.Row="8" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Name}"/>
<Label Content="Run:" Grid.Row="9"/>
<TextBox Grid.Row="9" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Run}"/>
<Label Content="Surface Flow Direction:" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].SurfaceFlowDirection}"/>
<Label Content="API Number:" Grid.Row="11"/>
<TextBox Grid.Row="11" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].APINumber}"/>
<Label Content="Distance To Navigable Waters:" Grid.Row="12"/>
<TextBox Grid.Row="12" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].DistanceToNavigableWaters}"/>
<Label Content="Project Phase:" Grid.Row="13"/>
<TextBox Grid.Row="13" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].APINumber}"/>
<Label Content="Unique ID:" Grid.Row="14"/>
<TextBox Grid.Row="14" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].UniqueID}"/>
<Label Content="Operational Field:" Grid.Row="15"/>
<TextBox Grid.Row="15" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].OperationalField}"/>
<Label Content="Facility Type:" Grid.Row="16"/>
<TextBox Grid.Row="16" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].FacilityType}"/>
<Label Content="Project Number:" Grid.Row="17"/>
<TextBox Grid.Row="17" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ProjectNumber}"/>
<Label Content="Project Phase:" Grid.Row="18"/>
<TextBox Grid.Row="18" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ProjectPhase}"/>
<Label Content="Report Number:" Grid.Row="19"/>
<TextBox Grid.Row="19" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ReportNumber}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ContainmentTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Name:"/>
<TextBox Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Name}"/>
<Label Content="Height:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Height}"/>
<Label Content="Diameter:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadHeight}"/>
<Label Content="County:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadWidth}"/>
<Label Content="Inner Length:" Grid.Row="4"/>
<TextBox Grid.Row="4" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadLength}"/>
<Label Content="Inner Width:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].InnerLength}"/>
<Label Content="Top Length:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].InnerWidth}"/>
<Label Content="Top Width:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TopLength}"/>
<Label Content="Construction:" Grid.Row="8"/>
<TextBox Grid.Row="8" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TopWidth}"/>
<Label Content="Fill Materials:" Grid.Row="9"/>
<TextBox Grid.Row="9" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Construction}"/>
<Label Content="Fill Material Height (Top):" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Diameter}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TanksTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Name:"/>
<TextBox Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Name}"/>
<Label Content="Height:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Height}"/>
<Label Content="Diameter:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Diameter}"/>
<Label Content="Is in Service:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].IsInService}"/>
<Label Content="Is on Pad:" Grid.Row="4"/>
<CheckBox Grid.Row="4" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsOnPad}"/>
<Label Content="Contents:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Contents}"/>
<Label Content="Year Constructed:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].YearConstructed}"/>
<Label Content="Material:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Material}"/>
<Label Content="Is Portable:" Grid.Row="8"/>
<CheckBox Grid.Row="8" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsPortable}"/>
<Label Content="Is Isolated:" Grid.Row="9"/>
<CheckBox Grid.Row="9" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsIsolated}"/>
<Label Content="Quantity:" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Quantity}"/>
<Label Content="Orientation:" Grid.Row="11"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Margin="5" Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TreeView Name="trvFacility" ItemsSource="{Binding MenuItems}" VirtualizingStackPanel.IsVirtualizing="True" Padding="10" Margin="5" Width="220">
<i:Interaction.Behaviors>
<local:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem,Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type entities:MenuItemCust}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ScrollViewer Grid.Row="0" Grid.Column="1" Margin="5" VerticalScrollBarVisibility="Visible">
DataTemplate here
</ScrollViewer>
<Button Content="Save" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Width="60" Height="25" Margin="0,0,5,0" HorizontalAlignment="Right"/>
</Grid>
MyWindowViewModel.cs
public class VisioFacilityExportViewModel : INotifyPropertyChanged
{
public VisioFacilityExportViewModel()
{
_lstFacilities = new ObservableCollection<Facility>(DatabaseHandler.GetFacilities().Where(f => f.FacilityID == 2015012314001933));
_lstContainments = _lstFacilities[0].Containment;
_lstTanks = _lstContainments[0].Tanks;
List<MenuItemCust> lstRoot = new List<MenuItemCust>();
List<MenuItemCust> lstContainment = new List<MenuItemCust>();
List<MenuItemCust> lstTanks = new List<MenuItemCust>();
MenuItems = new List<MenuItemCust>();
foreach (var f in _lstFacilities)
{
foreach (var c in f.Containment)
{
lstTanks.AddRange(c.Tanks.Select(t => new MenuItemCust { Name = t.Name, Info = "Tanks"}));
lstContainment.Add(new MenuItemCust { Name = c.Name, Children = lstTanks, Info = "Containment" });
}
lstRoot.Add(new MenuItemCust { Name = "Containment", Children = lstContainment, Info = "Facility"});
MenuItems.Add(new MenuItemCust { Name = "Facility", Children = lstRoot });
}
}
private ObservableCollection<Tank> _lstTanks;
public ObservableCollection<Tank> ListTanks
{
get { return _lstTanks; }
set
{
_lstTanks = value;
NotifyPropertyChanged("ListTanks");
}
}
private ObservableCollection<Containment> _lstContainments;
public ObservableCollection<Containment> ListContainments
{
get { return _lstContainments; }
set
{
_lstContainments = value;
NotifyPropertyChanged("ListContainments");
}
}
private ObservableCollection<Facility> _lstFacilities;
public ObservableCollection<Facility> ListFacilities
{
get { return _lstFacilities; }
set
{
_lstFacilities = value;
NotifyPropertyChanged("ListFacilities");
}
}
private List<MenuItemCust> _menuItems;
public List<MenuItemCust> MenuItems
{
get { return _menuItems; }
set
{
_menuItems = value;
NotifyPropertyChanged("MenuItems");
}
}
private static object _selectedItem = null;
public static object SelectedItem
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnSelectedItemChanged();
}
}
}
public static void OnSelectedItemChanged()
{
var item = (MenuItemCust) SelectedItem;
if (item.Name == "Facility")
{
}
else if (item.Info == "Containment")
{
}
else if (item.Info == "Tanks")
{
}
}
#region iNotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
}
I think what you want to do is display informations in a UserControl which will contains a grid with differents controls like textblock and textbox.
So you are going to create a new UserControl named FacilityView and FacilityViewModel and the FacilityView will contains a grid with ur Facility Ojbect property
First of all, I would create an property in MyWindowViewModel called
private Facility currentFacility;
public Facility CurrentFacility
{
get{return currentFacility;}
set{ currentFacility = value ;
notifyPropertyChange("CurrentFacility")
}
In your MainWindowsView (xaml)
<DataTemplate DataType="{x:Type localAdder:FacilityViewModel}">
<localAdder:FacilityView />
</DataTemplate>
<UserControl Grid.Row="1" Grid.Column="2"
Content="{Binding CurrentFacility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}">
</UserControl>
And in your SelectedItem you would
if (selectedItem is Facility)
{
CurrentFacility = new FacilityViewModel(selectedItem as Facility);
}
Sry for the bad indentation im not use to this writing code.
EDIT2:
Okay, you have 2 choice, first of all u can create a BaseViewModel which will implements INotifyPropertyChanged.
FacilityViewModel : BaseViewModel
TankViewModel : BaseViewModel
ContainmentViewModel : BaseViewModel
This way you will change CurrentFacilityViewModel to CurrentBaseViewModel
private BaseViewModel currentBaseXXX;
public BaseViewModel CurrentBaseXXX
{
get{return currentBaseXXX;}
set{ currentBaseXXX= value ;
notifyPropertyChange("CurrentBaseXXX")
}
And in your SelectedItem you would
if (selectedItem is Facility)
{
CurrentBaseXXX= new FacilityViewModel(selectedItem as Facility);
}
if (selectedItem is Tank)
{
CurrentBaseXXX= new TankViewModel(selectedItem as Tank);
}
if (selectedItem is Containers)
{
CurrentBaseXXX= new ContainersViewModel(selectedItem as Containers);
}
Options2
You would have 3 current so
CurrentFacilityVM
CurrentTankVM
CurrentContainerVM
And in your SelectedItem you would
CurrentFacilityVM = null;
CurrentTankVM =null;
CurrentContainerVM =null;
if (selectedItem is Facility)
{
CurrentFacilityVM = new FacilityViewModel(selectedItem as Facility);
}
if (selectedItem is Tank)
{
CurrentTankVM = new TankViewModel(selectedItem as Tank);
}
if (selectedItem is Containers)
{
CurrentContainerVM = new ContainersViewModel(selectedItem as Containers);
}
You can dynamically Load the data template based on Type. Refer the below. You can toggle the template by change the instance of the SelectedItem to a Different class.
<Window.Resources>
<DataTemplate DataType="{x:Type entities:Facility}">
<Grid>
<TextBlock Text="{Binding FacilityName}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type entities:Containment}">
<Grid>
<TextBlock Text="{Binding ContainmentName}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type entities:Tank}">
<Grid>
<TextBlock Text="{Binding TankName}"></TextBlock>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Margin="5" Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="1" Margin="5" VerticalScrollBarVisibility="Visible" DataContext="{Binding SelectedItem}">
<ContentControl Content="{Binding }"/>
</ScrollViewer>
<Button Content="Save" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Width="60" Height="25" Margin="0,0,5,0" HorizontalAlignment="Right"/>
</Grid>
public partial class Download : Window
{
public Download()
{
InitializeComponent();
this.DataContext = new VisioFacilityExportViewModel();
}
}
public class VisioFacilityExportViewModel : INotifyPropertyChanged
{
public VisioFacilityExportViewModel()
{
//SelectedItem = new Facility() { FacilityName = "Facility" };
//SelectedItem = new Tank() { TankName = "Tank" };
SelectedItem = new Containment() { ContainmentName = "Containment" };
}
private object _selectedItem = null;
public object SelectedItem
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
#region iNotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class Tank
{
private string myVar;
public string TankName
{
get { return myVar; }
set { myVar = value; }
}
}
class Facility
{
private string myVar;
public string FacilityName
{
get { return myVar; }
set { myVar = value; }
}
}
class Containment
{
private string myVar;
public string ContainmentName
{
get { return myVar; }
set { myVar = value; }
}
}