Hi all I have a page that I'm trying to ADD to a list and save it with viewmodel and SQL. but my list is empty Can you tell me where am I wrong??
on my Xaml (Page1):
<Entry Margin="5" Text="{Binding Xname}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" FontSize="16" x:Name="entry1" Placeholder="Enter X-rays (6 MV)" PlaceholderColor="White" BackgroundColor="Gray"/>
<Button Grid.Column="0"
Grid.Row="2"
FontAttributes="Bold"
Command="{Binding AddXrayCommand}"
Text="Add" />
<ListView Grid.Row="3" Grid.ColumnSpan="2" HasUnevenRows="True" Margin="40"
ItemsSource="{Binding energyX}" SelectedItem="{Binding selecteditemX}"
HorizontalOptions="Start" WidthRequest="150" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" BackgroundColor="White" Text="{Binding xray}" FontSize="Large" HorizontalTextAlignment="Center" TextColor="Black" WidthRequest="35"/>
<Frame Grid.Row="1" BackgroundColor="Gray"></Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
my Model (Energypage):
public class EnergyX
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string xray { get; set; }
}
And on my EnergyViewModel:
public class EnergyViewModel
{
public SQLiteConnection conn;
public ObservableCollection<EnergyX> energyX { get; set; } = new ObservableCollection<EnergyX>();
public string Xname { get; set; }
public ICommand AddXrayCommand => new Command(AddX);
public SQLiteConnection GetSQLiteConnection()
{
var fileName = "Energys.db";
var documentPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
var path = Path.Combine(documentPath, fileName);
var connection = new SQLiteConnection(path);
return connection;
}
public void AddX()
{
if (Xname != null && Xname.Length > 0)
{
EnergyX XX = new EnergyX();
XX.xray = Xname;
conn = GetSQLiteConnection();
conn.CreateTable<EnergyX>();
var dataX = conn.Table<EnergyX>();
var resultX = conn.Insert(XX);
energyX = new ObservableCollection<EnergyX>(conn.Table<EnergyX>().ToList());
}
}
}
}
I did bond my page1 to Energyviewmodel, and it works find when I didn't use SQL service, I think my SQL has problem...
I have debug the viewmodel and program run to the end of the Viewmodel but my xaml page list is empty.
this line creates a completely new instance of energyX when your ListView is bound to the old instance
energyX = new ObservableCollection(conn.Table().ToList());
there are two ways you could fix this
option 1, use INotifyPropertyChanged and raise a PropertyChanged event in the setter of energyX
option 2, don't create a new instance of energyX. Instead just add the new item to the existing instance
energyX.Add(XX);
Related
I have ViewModel with proporties and method to populate values:
public class MRateReportViewModel : HeaderViewModel
{
public ICommand ChangeReportPeriodCommand { get; }
public ICommand BackToHomePageCommand { get; }
public string Hashtag { get; set; }
public string Rating { get; set; }
public decimal? mTP { get; set; }
public string Image { get; set; }
public string Date { get; set; }
public DateTime MinimumDate { get; set; }
public DateTime MaximumDate { get; set; }
public DateTime PickedDate { get; set; }
public ObservableCollection<UserEventModel> UserEventsCollection { get; set; }
private UserEventsModel userEventsModel;
private ObservableCollection<EventTypeAtributtes> EventTypes;
public string EventType;
public List<String> EvtList { get; set; }
public MRateReportViewModel()
{
UserEventsCollection = new ObservableCollection<UserEventModel>();
userEventsModel = new UserEventsModel();
ChangeReportPeriodCommand = new Command((dailyOrTotal) => ChangeReportPeriod(dailyOrTotal.ToString()));
BackToHomePageCommand = new Command(() => BackToHomePage());
InitData();
}
private async void InitData()
{
ShowDialog();
userEventsModel = await EventService.GetEvents();
EventTypes = await EventService.GetEventType();
foreach (var item in userEventsModel.Events)
{
UserEventsCollection.Add(item);
}
HideDialog();
Page++;
}
}
By debugging I checked EventDetails methods and proporties get correct values.
In View I have page with ListView which I should populate with values from viewmodel:
<ListView ItemsSource="{Binding EventsList}"
CachingStrategy="RecycleElement"
x:Name="EventsDiary"
ItemAppearing="EventsDiary_ItemAppearing"
SelectionMode="None"
HasUnevenRows="True"
Margin="0,0,0,10">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<material:MaterialCard HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
CornerRadius="2"
Margin="10,0,10,15"
HeightRequest="178"
Padding="0"
BackgroundColor="#f4f4f4">
<Grid RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="6" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<BoxView Grid.Column="0"
Grid.ColumnSpan="5"
Grid.Row="0"
BackgroundColor="{StaticResource CustomizedRedColor}"
CornerRadius="4"
Margin="0" />
<RelativeLayout Grid.Column="0"
Grid.Row="1"
Margin="10,10,0,0">
<controls:CircleImage HeightRequest="90"
WidthRequest="90"
Source="serpa1.png"
Aspect="AspectFill">
</controls:CircleImage>
<material:MaterialCard HeightRequest="30"
WidthRequest="30"
CornerRadius="50"
BackgroundColor="#525252"
Margin="0"
Padding="0"
Opacity="0.9">
<Label Text="1"
TextColor="White"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" />
</material:MaterialCard>
</RelativeLayout>
<StackLayout Grid.Column="1"
Grid.ColumnSpan="3"
Grid.Row="1"
Padding="0"
Margin="0"
Orientation="Vertical"
Spacing="0">
<Label Text="Ključne reči"
FontSize="18"
Margin="0,5,0,0"
TextColor="#03414e"
FontFamily="{StaticResource BalooBhai}" />
<Label Text="{Binding Hashtag}"
FontSize="12"
Margin="0"
VerticalOptions="StartAndExpand"
TextColor="#030303" />
This is part of xaml code. In this example I should bind Hashtag value but it just remain blank.
Here is my service for UserEventsModel:
public static async Task<UserEventsModel> GetEvents()
{
UserEventsModel userEventModel = new UserEventsModel();
try
{
//string url = UrlConstants.GET_USER_EVENTS;
string url = UrlConstants.USER_DIARY;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Cookie", LocalDataHelper.RestoreCookie());
string content = Newtonsoft.Json.JsonConvert.SerializeObject(userEventModel);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var result = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json")))
{
string resultContent = await result.Content.ReadAsStringAsync();
if (result.StatusCode == System.Net.HttpStatusCode.OK)
{
userEventModel = (UserEventsModel)JsonConvert.DeserializeObject<UserEventsModel>(resultContent);
}
}
}
}
catch (Exception ex)
{
throw;
}
return userEventModel;
}
Do your class or base class implements the INotifyPropertyChanged interface?
like this:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
}
public class YOURViewModel : BaseViewModel
{
private string _Hashtag;
public string Hashtag
{
get { return _Hashtag; }
set { _Hashtag = value; OnPropertyChanged(); }
}
}
According to your code, you want to bind ViewModel to ListView, firstly, you should need to know ListView is collection control, so you shoule bind collection to ListView's itemsource.
From your code, you want to use EventsList to bind ListView, but I don't see where you populate data into EventsList?
I see there is userEventsModel ViewModel, Events collection in this, but why you foreach this collection?
I change you Viewmodel to do one sample for you, please take a look:
public partial class Page9 : ContentPage
{
public ObservableCollection<UserEventModel> EventsList { get; set; }
public Page9 ()
{
InitializeComponent ();
//populate data in collection EventsList.
EventsList = new ObservableCollection<UserEventModel>()
{
new UserEventModel(){Hashtag="test1",Rating="test1",mTP=0, Image="test1",Date="test1"}
};
this.BindingContext = this;
}
}
public class UserEventModel
{
public string Hashtag { get; set; }
public string Rating { get; set; }
public decimal? mTP { get; set; }
public string Image { get; set; }
public string Date { get; set; }
}
The ListView is in Page 9,
<ListView ItemsSource="{Binding EventsList}"
CachingStrategy="RecycleElement"
x:Name="EventsDiary"
ItemAppearing="EventsDiary_ItemAppearing"
SelectionMode="None"
HasUnevenRows="True"
Margin="0,0,0,10">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<material:MaterialCard HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
CornerRadius="2"
Margin="10,0,10,15"
HeightRequest="178"
Padding="0"
BackgroundColor="#f4f4f4">
<Grid RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="6" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<BoxView Grid.Column="0"
Grid.ColumnSpan="5"
Grid.Row="0" BackgroundColor="{StaticResource CustomizedRedColor}"
CornerRadius="4"
Margin="0" />
<RelativeLayout Grid.Column="0"
Grid.Row="1"
Margin="10,10,0,0">
<controls:CircleImage HeightRequest="90"
WidthRequest="90"
Source="serpa1.png"
Aspect="AspectFill">
</controls:CircleImage>
<material:MaterialCard HeightRequest="30"
WidthRequest="30"
CornerRadius="50"
BackgroundColor="#525252"
Margin="0"
Padding="0"
Opacity="0.9">
<Label Text="1"
TextColor="White"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" />
</material:MaterialCard>
</RelativeLayout>
<StackLayout Grid.Column="1"
Grid.ColumnSpan="3"
Grid.Row="1"
Padding="0"
Margin="0"
Orientation="Vertical"
Spacing="0">
<Label Text="Ključne reči"
FontSize="18"
Margin="0,5,0,0"
TextColor="#03414e"
FontFamily="{StaticResource BalooBhai}" />
<Label Text="{Binding Hashtag}"
FontSize="12"
Margin="0"
VerticalOptions="StartAndExpand"
TextColor="#030303" />
</StackLayout>
</Grid>
</material:MaterialCard>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There are one article about bind to ListView, you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/data-and-databinding
https://almirvuk.blogspot.com/2017/02/xamarinforms-listview-simple-mvvm.html
I'm trzuying to show diferent images in ListView frames, depending of ServiceName in my model.
So here is my model:
public class IrrigNetModelItem
{
public int ID { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
public string DateText { get; set; }
public int StationId { get; set; }
public string StationName { get; set; }
public float StationLongitude { get; set; }
public float StationLatitude { get; set; }
public int ServiceId { get; set; }
public string ServiceName { get; set; }
}
And here is part of ViewModel
public ObservableCollection<IrrigNetModelItem> IrrigNetCollection { get; set; } = new ObservableCollection<IrrigNetModelItem>();
public async void GetData()
{
base.OnAppearing();
dialog.Show();
var token = LocalData.Token;
var data = await IrrigNetService.GetServices(token, "en");
var irrigNetModel = new IrrigNetModelItem();
foreach (var d in data.items)
{
IrrigNetCollection.Add(d);
if (d.ServiceName == "irrigNET")
{
IrrigCounter++;
//FrameImage = "service_irrig_img.png";
//FrameHeaderColor = Color.FromHex("#33A8F7");
}
else
{
AlertCounter++;
//FrameImage = "alert.png";
//FrameHeaderColor = Color.FromHex("#2BB24B");
}
}
dialog.Hide();
}
For now, Service name could be "irrigNET" and "alertNET" and depending of that I want to set diferent image source in my ListView in View.
Here is View:
<ListView
ItemsSource="{Binding IrrigNetCollection}"
IsVisible="{Binding IsListVisible}"
ItemSelected="FrameList_ItemSelected"
HasUnevenRows="False"
x:Name="FrameList"
Grid.Row="2"
RowHeight="190"
Margin="0,0,0,20"
SeparatorVisibility="None"
HeightRequest="0">
<ListView.ItemTemplate Padding="0">
<DataTemplate>
<ViewCell>
<Frame
HasShadow="True"
Grid.ColumnSpan="5"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="#f4f4f4"
BorderColor="LightGray"
CornerRadius="10"
Margin="25,10,25,10"
Padding="0">
<Grid
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
IsEnabled="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="13"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<BoxView Color="{Binding Path=BindingContext.FrameHeaderColor, Source={x:Reference FrameList}}"
Grid.Row="0"
Grid.ColumnSpan="6"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"/>
<Image Source="{Binding Path=BindingContext.FrameImage, Source={x:Reference FrameList}}"
Grid.Column="1"
Grid.Row="1"
Grid.RowSpan="3"
Margin="0,20,0,0"/>
<Image Source="{Binding Path=BindingContext.FrameIcon, Source={x:Reference FrameList}}"
Grid.Column="2"
Grid.Row="1"
HorizontalOptions="Start"
VerticalOptions="Center"
HeightRequest="17"
WidthRequest="17"/>
<Label
Text="{Binding StationName}"
VerticalTextAlignment="Start"
HorizontalTextAlignment="Start"
HorizontalOptions="Start"
VerticalOptions="Center"
Margin="3,3,0,0"
FontSize="18"
Grid.Column="3"
Grid.Row="1"
FontFamily="{StaticResource BalooBhai}"
TextColor="#262f41"/>
<Image Source="service_arrow.png"
Grid.Column="4"
Grid.Row="2"
VerticalOptions="Center"
HorizontalOptions="Center"
HeightRequest="18"
WidthRequest="18"/>
<Image Source="clock.png"
Grid.Column="2"
Grid.Row="3"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<Label Text="{Binding DateText}"
FontSize="14"
FontFamily="{StaticResource SegoeUIB}"
Grid.Column="3"
Grid.Row="3"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Start"
Margin="3,0,0,0"
TextColor="#262f41"/>
<Label Text="{Binding Message}"
Grid.Column="3"
Grid.Row="2"
VerticalTextAlignment="Start"
HorizontalOptions="Center"
VerticalOptions="Center"
FontFamily="{StaticResource SegoeUI}" FontSize="13"
TextColor="#262f41"
Margin="0,0,10,0"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="40">
<StackLayout Orientation="Horizontal"
BackgroundColor="#3498DB"
VerticalOptions="FillAndExpand">
<Label Text="TeStIrAnjE"
TextColor="White"
VerticalOptions="Center" />
<Button Text="Edit"
TextColor="White"
FontSize="20"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
You'll se what I tried in Image tags, but for now any image ar not show.
This is proporties that I try to use in my ViewModel:
//public string FrameIrrigImage { get; set; } //= "service_irrig_img.png";
//public Color FrameIrrigHeaderColor { get; set; } //= Color.FromHex("#33A8F7");
//public string FrameAlertImage { get; set; } //= "alert.png";
//public Color FrameAlertHeaderColor { get; set; } // = Color.FromHex("#2BB24B");
//public string typeNet { get; set; }
//public Color typeNETcolortext { get; set; }
//public Color allertNETcolortext { get; set; }
This is what I want to achive:
But this is what I get:
(As you can see BoxView heder is also diferent color blue/color but I will get it how to change it on Image example)
Also I tried to achive it using properties:
public string FrameImage { get; set; }
public Color FrameHeaderColor { get; set; }
Set them values in for loop and Bind them in xaml, but then it's all set (image and color) as for the last element in ListView
As Jason said, there are two way to do this, one is use IValueConverter by ServiceName, another is add FrameImage in model, then change FrameImage by serviceName.
I do one simple that you can take a look,if you add FrameImage in model, please implement INotifyPropertyChanged interface.
<ContentPage
x:Class="App4.Page9"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:App4">
<ContentPage.Resources>
<converter:convert1 x:Key="converterimage" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding model2s}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding firstname}" />
<Image Source="{Binding imagepath}" />
<Image Source="{Binding Id, Converter={StaticResource converterimage}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class Page9 : ContentPage
{
public ObservableCollection<model2> model2s { get; set; }
public Page9 ()
{
InitializeComponent ();
model2s = new ObservableCollection<model2>()
{
new model2(){Id=1,firstname="cherry"},
new model2(){Id=2,firstname="mattew"},
new model2(){Id=3,firstname="annine"},
new model2(){Id=4,firstname="barry"}
};
foreach(model2 m in model2s)
{
if(m.Id%2==0)
{
m.imagepath = "a1.jpg";
}
else
{
m.imagepath = "a2.jpg";
}
}
this.BindingContext = this;
}
}
public class model2:ViewModelBase
{
public int Id { get; set; }
public string firstname { get; set; }
private string _imagepath;
public string imagepath
{
get { return _imagepath; }
set
{
_imagepath = value;
RaisePropertyChanged("imagepath");
}
}
}
Converter:
class convert1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int id = (int)value;
if(id%2==0)
{
return "a1.jpg";
}
else
{
return "a2.jpg";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have had a lot of help so far with creating the correct formatting for an ItemsControl panel and appreciate this communities help so far with helping me troubleshoot coding issues.
Im currently at a rather small hurdle where im trying to figure out how to create multiple boxes within the same ItemsControl. Currently the overall view looks like this:
Im a little stumped and would just like a little guidance really as to where to put the other XAML lines.
I need it to look like this:
Here is my code currently (its all nested within a Frame):
<ItemsControl ItemsSource="{StaticResource userDataCollection}" Margin="40,40,40,725" Width="Auto" Height="310">
<!-- Changing Orientation of VirtualizingStackPanel -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Change header for ItemsControl -->
<ItemsControl.Template>
<ControlTemplate>
<Border Background="{StaticResource CustomAcrylicDarkBackground}">
<StackPanel>
<TextBlock Text="Accounts At A Glance" FontSize="28" Foreground="#b880fc" Padding="12"/>
<ItemsPresenter/>
</StackPanel>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!-- Template for each card-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="240" Height="240" Background="Gray" Margin="30,0,0,0" VerticalAlignment="Center" Padding="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="24"/>
<TextBlock Grid.Row="1" Text="{Binding PayDate}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14" />
<TextBlock Grid.Row="2" Text="{Binding NumberOfItems}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I really apologise for this, im trying to learn as much as i can as i go. Im mainly stuggling with the XAML formatting and incorperating learning material into my project :/ Any help would be amazing
I have an alternative approach for your problem. This uses "semi" MVVM approach (browse through the net and study this pattern).
MainPageViewModel.cs
public class MainPageViewModel : INotifyPropertyChanged
{
private ObservableCollection<User> _userCollection;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<User> UserCollection
{
get => _userCollection;
set
{
_userCollection = value;
NotifyProperyChanged();
}
}
private void NotifyProperyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void LoadData()
{
UserCollection = new ObservableCollection<User>
{
new User
{
Name = "John Doe",
PayDate = DateTime.Now,
NumberOfItems = 1
},
new User
{
Name = "John Doe 2",
PayDate = DateTime.Now,
NumberOfItems = 1
},
new User
{
Name = "John Doe 3",
PayDate = DateTime.Now,
NumberOfItems = 1
},
};
}
}
The view (got rid of some styling temporarily):
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:App1.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="MainPage_OnLoaded">
<Page.DataContext>
<vm:MainPageViewModel/>
</Page.DataContext>
<Grid>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding UserCollection, Mode=TwoWay}" Margin="15" Width="Auto" Height="310">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Template for each card-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="200" Height="100" Background="Gray" Margin="15,0,0,0" VerticalAlignment="Center" Padding="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="24"/>
<TextBlock Grid.Row="1" Text="{Binding PayDate}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14" />
<TextBlock Grid.Row="2" Text="{Binding NumberOfItems}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>
View's code-behind:
namespace App1
{
public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
}
public MainPageViewModel VM => (MainPageViewModel) DataContext;
private void MainPage_OnLoaded(object sender, RoutedEventArgs e)
{
VM.LoadData();
}
}
}
Output:
Next steps:
Apply your styling. Study how to limit grid columns.
Improve the code
further, in MVVM you shouldn't really have implementations on the
code-behind, so study for ICommand, Microsoft.Xaml.Interactivity
Hope this helps.
It perfect now.
Im an idiot.
I essentially needed to seperate the information presented within the UserData.cs Class. I didnt understand how the information was being read but understand it now. The XAML has been left untouched as it works currently for what i need. It will be update to follow the MVVM format as mentioned by Mac. Here is the UserData.CS class located inside a data folder:
using System.Collections.ObjectModel;
namespace BudgetSheet.Data
{
public class UserData
{
public string Name { get; set; }
public string PayDate { get; set; }
public string NumberOfItems { get; set; }
}
class UserDataCollection : ObservableCollection<UserData>
{
public UserDataCollection()
{
// Placeholder, needs to be replaced with CSV or Database information
this.Add(new UserData()
{
Name = "Selected Username",
PayDate = "Friday",
NumberOfItems = "ItemAmount Placeholder"
});
// Placeholder for user 2
this.Add(new UserData()
{
Name = "Selected Username 2",
PayDate = "Friday 2",
NumberOfItems = "ItemAmount Placeholder 2"
});
// Placeholder for user 3
this.Add(new UserData()
{
Name = "Selected Username 3",
PayDate = "Friday 3",
NumberOfItems = "ItemAmount Placeholder 3"
});
}
}
}
Here is what it was before hand and why there were issues with information display:
using System.Collections.ObjectModel;
namespace BudgetSheet.Data
{
public class UserData
{
public string Name { get; set; }
public string PayDate { get; set; }
public string NumberOfItems { get; set; }
}
class UserDataCollection : ObservableCollection<UserData>
{
public UserDataCollection()
{
// Placeholder, needs to be replaced with CSV or Database information
this.Add(new UserData()
{
Name = "Selected Username",
});
// Placeholder for user selected PayDate
this.Add(new UserData()
{
PayDate = "Friday",
});
// Placeholder for user selected PayDate
this.Add(new UserData()
{
NumberOfItems = "ItemAmount Placeholder"
});
}
}
}
This solution does not provide much flexibility currently but it works for the formatting. Marking as resolved to close the ticket
When using a SemanticZoom control, is there a way to update the ObservableCollection in the ViewModel after a table change? After making changes to the table in SQLite, within the same page (categories.xaml.cs), the SemanticZoom control does not update. Reloading the page from menu navigation does reload the page with the correct data. If the control just took an ObservableCollection as it's items source, the ObservableCollection could just be refreshed. Using a ViewModel was the only code example I could find for the SemanticZoom control. Thanks in advance!
categories.xaml
<Page.DataContext>
<vm:CategoriesViewModel></vm:CategoriesViewModel>
</Page.DataContext>
<Page.Resources>
<CollectionViewSource x:Name="Collection" IsSourceGrouped="true" ItemsPath="Items" Source="{Binding CategoryGroups}" />
</Page.Resources>
<SemanticZoom Name="szCategories" ScrollViewer.ZoomMode="Enabled">
<SemanticZoom.ZoomedOutView>
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Group.Name }" Foreground="Gray" Margin="5" FontSize="25" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<ListView Name="lvCategories" ItemsSource="{Binding Source={StaticResource Collection}}" Tapped="lvCategories_Tapped">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Category">
<StackPanel>
<TextBlock Text="{Binding Title}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text='{Binding Name}' Foreground="Gray" FontSize="25" Margin="5,5,5,0" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
categories.xaml.cs
public Categories()
{
this.InitializeComponent();
var collectionGroups = Collection.View.CollectionGroups;
((ListViewBase)this.szCategories.ZoomedOutView).ItemsSource = collectionGroups;
}
CategoriesViewModel.cs
internal class CategoriesViewModel : BindableBase
{
public CategoriesViewModel()
{
CategoryGroups = new ObservableCollection<CategoryDataGroup>(CategoryDataGenerator.GetGroupedData());
}
private ObservableCollection<CategoryDataGroup> _groups;
public ObservableCollection<CategoryDataGroup> CategoryGroups
{
get { return _groups; }
set { SetProperty(ref _groups, value); }
}
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
SymanticZoom.cs
internal class CategoryDataGroup
{
public string Name { get; set; }
public List<CategoryData> Items { get; set; }
}
internal class CategoryData
{
public CategoryData(string grp, string title)
{
Grp = grp;
Title = title;
}
public string Grp { get; private set; }
public string Title { get; private set; }
}
internal class CategoryDataGenerator
{
private static List<CategoryData> _data;
public static List<CategoryDataGroup> GetGroupedData()
{
if (_data != null)
_data.Clear();
GenerateData();
return _data.GroupBy(d => d.Grp[0],
(key, items) => new CategoryDataGroup() { Name = key.ToString(), Items = items.ToList() }).ToList();
}
private static void GenerateData()
{
ObservableCollection<Category> ocCategories = new ObservableCollection<Category>();
SQLiteManager.Categories.Select(ocCategories);
_data = new List<CategoryData>();
foreach (var temp in ocCategories)
{
_data.Add(new CategoryData(temp.Name.Substring(0,1), temp.Name));
}
}
}
The zoomed-in view and zoomed-out view should be synchronized, so if a user selects a group in the zoomed-out view, the details of that same group are shown in the zoomed-in view. You can use a CollectionViewSource or add code to synchronize the views.
For more info, see Semantic zoom.
We can use CollectionViewSource control in our page, it provides a data source that adds grouping and current-item support to collection classes. Then we can bind the GridView.ItemSource and ListView.ItemSource to the CollectionViewSource. When we set new data to the CollectionViewSource, the GridView in SemanticZoom.ZoomedOutView and ListView in SemanticZoom.ZoomedInView will be updated.
xmlns:wuxdata="using:Windows.UI.Xaml.Data">
<Page.Resources>
<CollectionViewSource x:Name="ContactsCVS" IsSourceGrouped="True" />
<DataTemplate x:Key="ZoomedInTemplate" x:DataType="data:Contact">
<StackPanel Margin="20,0,0,0">
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Position}" TextWrapping="Wrap" HorizontalAlignment="Left" Width="300" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ZoomedInGroupHeaderTemplate" x:DataType="data:GroupInfoList">
<TextBlock Text="{x:Bind Key}"/>
</DataTemplate>
<DataTemplate x:Key="ZoomedOutTemplate" x:DataType="wuxdata:ICollectionViewGroup">
<TextBlock Text="{x:Bind Group.(data:GroupInfoList.Key)}" TextWrapping="Wrap"/>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<SemanticZoom x:Name="Control1" Height="500">
<SemanticZoom.ZoomedInView>
<GridView ItemsSource="{x:Bind ContactsCVS.View,Mode=OneWay}" ScrollViewer.IsHorizontalScrollChainingEnabled="False" SelectionMode="None"
ItemTemplate="{StaticResource ZoomedInTemplate}">
<GridView.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource ZoomedInGroupHeaderTemplate}" />
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView ItemsSource="{x:Bind ContactsCVS.View.CollectionGroups}" SelectionMode="None" ItemTemplate="{StaticResource ZoomedOutTemplate}" />
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</StackPanel>
</Grid>
I've seen something along these lines done with {DynamicResource xyz}on some other SO question, but it doesn't seem to work with UWP. Here's my XAML:
<DataTemplate x:Key="commentTemplate">
<StackPanel>
<Grid Margin="4" Background="#40606060" MinHeight="64">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Margin="4" FontSize="14" TextWrapping="WrapWholeWords" Text="{Binding Path=Message, Mode=OneWay}" />
<Image MaxHeight="96" Source="{Binding Path=Image}" HorizontalAlignment="Left" Margin="4" />
</StackPanel>
<StackPanel Background="#18808080" Orientation="Horizontal" Grid.Row="1">
<FontIcon Margin="2,0" Glyph="" />
<TextBlock Margin="2,0" Text="{Binding Path=Score, Mode=OneWay}" />
<Button Content="Reply" Margin="2,0" />
</StackPanel>
</Grid>
<ItemsControl ItemTemplate="{RelativeSource Mode=Self}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Margin="48" ItemsSource="{Binding Path=Comments}" />
</StackPanel>
</DataTemplate>
I'd like to self-reference the DataTemplate in the ItemsControl's ItemTemplate property. How would I go about replacing it?
Here is something that may work for you. I use this in our POS application - although this is my first attempt at nesting it. The xaml designer complains because of the selector StaticResource reference prior to its declaration, but it works at runtime.
Essentially, I use this to select a different data template based on the class that is bound to the item. In this case, there is only one type, so really we are just using the class type to decide what template to use.
MyModel:
public class MyModel
{
public int Id { get; set; }
public string Desc { get; set; }
public List<MyModel> Children { get; set; }
}
MyTemplateSelector:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate MyModelTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is MyModel)
{
return MyModelTemplate;
}
else
{
return null;
}
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return SelectTemplateCore(item);
}
}
My MainPage:
<Page
x:Class="App7.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App7"
xmlns:models="using:App7.Models"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="myModelTemplate" x:DataType="models:MyModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind Path=Desc, Mode=OneWay}" />
<ListView Grid.Row="1" ItemTemplateSelector="{StaticResource selector}" ItemsSource="{x:Bind Path=Children, Mode=OneWay}" />
</Grid>
</DataTemplate>
<local:MyTemplateSelector x:Key="selector" MyModelTemplate="{StaticResource myModelTemplate}" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="lstView" ItemTemplateSelector="{StaticResource selector}" >
</ListView>
</Grid>
</Page>
My code behind:
public sealed partial class MainPage : Page
{
List<MyModel> lst { get; set; }
public MainPage()
{
this.InitializeComponent();
BuildList();
lstView.ItemsSource = lst;
}
private void BuildList()
{
lst = new List<MyModel>();
for (int i = 0; i < 10; i++)
{
MyModel mod = new MyModel();
mod.Id = i;
mod.Desc = "Desc" + i.ToString();
mod.Children = new List<MyModel>();
for (int j = 100; j < 102; j++)
{
MyModel mod2 = new MyModel();
mod2.Id = j;
mod2.Desc = "Desc" + j.ToString();
mod2.Children = new List<MyModel>();
for (int k = 1000; k < 1002; k++)
{
MyModel mod3 = new MyModel();
mod3.Id = k;
mod3.Desc = "Desc" + k.ToString();
mod3.Children = new List<MyModel>();
mod2.Children.Add(mod3);
}
mod.Children.Add(mod2);
}
lst.Add(mod);
}
}
}