So, I am on my way learning MVVM Pattern for Windows Phone, and stuck how to bind the View to my ViewModel. App that I build now is getting current and next 5 days weather and display it to one of my panorama item on MainPage.xaml using UserControl.
I cannot just simply set the Forecasts.ItemsSource = forecast; in my WeatherViewModel, it says that Forecasts (Listbox element name in WeatherView) not exist in the current context.
Can anybody teach me how to bind it? and anybody have a good source/example sample to mvvm pattern in windows-phone? Thanks before.
EDIT:
WeatherModel.cs
namespace JendelaBogor.Models
{
public class WeatherModel
{
public string Date { get; set; }
public string ObservationTime { get; set; }
public string WeatherIconURL { get; set; }
public string Temperature { get; set; }
public string TempMaxC { get; set; }
public string TempMinC { get; set; }
public string Humidity { get; set; }
public string WindSpeedKmph { get; set; }
}
}
WeatherViewModel.cs
namespace JendelaBogor.ViewModels
{
public class WeatherViewModel : ViewModelBase
{
private string weatherURL = "http://free.worldweatheronline.com/feed/weather.ashx?q=";
private const string City = "Bogor,Indonesia";
private const string APIKey = "APIKEY";
private IList<WeatherModel> _forecasts;
public IList<WeatherModel> Forecasts
{
get
{
if (_forecasts == null)
{
_forecasts = new List<WeatherModel>();
}
return _forecasts;
}
private set
{
_forecasts = value;
if (value != _forecasts)
{
_forecasts = value;
this.NotifyPropertyChanged("Forecasts");
}
}
}
public WeatherViewModel()
{
WebClient downloader = new WebClient();
Uri uri = new Uri(weatherURL + City + "&num_of_days=5&extra=localObsTime&format=xml&key=" + APIKey, UriKind.Absolute);
downloader.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ForecastDownloaded);
downloader.DownloadStringAsync(uri);
}
private void ForecastDownloaded(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Result == null || e.Error != null)
{
MessageBox.Show("Cannot load Weather Forecast!");
}
else
{
XDocument document = XDocument.Parse(e.Result);
var current = from query in document.Descendants("current_condition")
select new WeatherModel
{
ObservationTime = DateTime.Parse((string)query.Element("localObsDateTime")).ToString("HH:mm tt"),
Temperature = (string)query.Element("temp_C"),
WeatherIconURL = (string)query.Element("weatherIconUrl"),
Humidity = (string)query.Element("humidity"),
WindSpeedKmph = (string)query.Element("windspeedKmph")
};
this.Forecasts = (from query in document.Descendants("weather")
select new WeatherModel
{
Date = DateTime.Parse((string)query.Element("date")).ToString("dddd"),
TempMaxC = (string)query.Element("tempMaxC"),
TempMinC = (string)query.Element("tempMinC"),
WeatherIconURL = (string)query.Element("weatherIconUrl")
}).ToList();
}
}
}
}
WeatherView.xaml
<UserControl x:Class="JendelaBogor.Views.WeatherView"
xmlns:vm="clr-namespace:JendelaBogor.ViewModels">
<UserControl.DataContext>
<vm:WeatherViewModel />
</UserControl.DataContext>
<Grid Margin="0,-10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="Current" Grid.Row="0" Height="150" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" delay:LowProfileImageLoader.UriSource="{Binding WeatherIconURL}" Width="120" Height="120" VerticalAlignment="Top"/>
<StackPanel Grid.Column="1" Height="200" VerticalAlignment="Top">
<TextBlock Text="{Binding Temperature}" FontSize="22"/>
<TextBlock Text="{Binding ObservationTime}" FontSize="22"/>
<TextBlock Text="{Binding Humidity}" FontSize="22"/>
<TextBlock Text="{Binding Windspeed}" FontSize="22"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1" Height="300" VerticalAlignment="Bottom" Margin="10,0,0,0">
<StackPanel VerticalAlignment="Top">
<StackPanel Height="40" Orientation="Horizontal" Margin="0,0,0,0">
<TextBlock Text="Date" FontSize="22" Width="170"/>
<TextBlock Text="FC" FontSize="22" Width="60"/>
<TextBlock Text="Max" TextAlignment="Right" FontSize="22" Width="90"/>
<TextBlock Text="Min" TextAlignment="Right" FontSize="22" Width="90"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding Forecasts}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="40" Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Text="{Binding Date}" FontSize="22" TextAlignment="Left" Width="170" />
<Image delay:LowProfileImageLoader.UriSource="{Binding WeatherIconURL}" Width="40" Height="40" />
<TextBlock Text="{Binding TempMaxC, StringFormat='\{0\} °C'}" TextAlignment="Right" FontSize="22" Width="90" />
<TextBlock Text="{Binding TempMinC, StringFormat='\{0\} °C'}" TextAlignment="Right" FontSize="22" Width="90" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</UserControl>
MainPage.xaml
<controls:PanoramaItem x:Name="Weather" Header="weather">
<views:WeatherView />
</controls:PanoramaItem>
You need to tell the view what viewmodel you are using. By adding
<UserControl
xmlns:vm="clr-namespace:JendelaBogor.ViewModels">
<UserControl.DataContext>
<vm:WeatherViewModel />
</UserControl.DataContext>
</UserControl>
all {Binding}'s are mapped to the class WeatherViewModel. By using the ItemsSource property on the listbox as Reed suggests you can then bind all items from a list that you expose through a property.
If the list is ever changed while running the application, consider using an ObservableCollection and clearing it and adding all new items when new data is received. If you do, your GUI will simply update with it.
The ViewModel doesn't know about the view.
You need to make a Forecasts property on the ViewModel, and bind the ItemsSource to it from your View. In your view, change the ListBox to:
<!-- No need for a name - just add the binding -->
<ListBox ItemsSource="{Binding Forecasts}">
Then, in your ViewModel, add:
// Add a backing field
private IList<WeatherModel> forecasts;
// Add a property implementing INPC
public IList<WeatherModel> Forecasts
{
get { return forecasts; }
private set
{
forecasts = value;
this.RaisePropertyChanged("Forecasts");
}
}
You can then set this in your method:
this.Forecasts = (from query in document.Descendants("weather")
select new WeatherModel
{
Date = DateTime.Parse((string)query.Element("date")).ToString("dddd"),
TempMaxC = (string)query.Element("tempMaxC"),
TempMinC = (string)query.Element("tempMinC"),
WeatherIconURL = (string)query.Element("weatherIconUrl")
})
.ToList(); // Turn this into a List<T>
Related
I want to add inside my Datagrid-Rowdetails a Listview with my loaded imageURls from my firebase Cloud.
In my method I recieve all data from firebase and convert it into a user object. The Url-array get's appended into a list and converted to the user object.
Here is the method:
async void getAllData() {
Query docref = database.Collection("users");
QuerySnapshot snap = await docref.GetSnapshotAsync();
foreach (DocumentSnapshot docsnap in snap){
Users Employee = docsnap.ConvertTo<Users>();
if (docsnap.Exists) {
List<string> AuthorList = new List<string>();
string UrlLinks = "";
for (int i = 0; i < Employee.ImageUrl.Length; i++) {
string URLS = Employee.ImageUrl[i].ToString();
UrlLinks += URLS + Environment.NewLine;
AuthorList.Add(URLS);
Employee.imagepath = AuthorList;
}
// Every URL links get printed out MessageBox.Show(UrlLinks);
DataGridXAML.Items.Add(Employee);
}
}
}
My User-Class:
namespace First_WPF_Project
{
[FirestoreData]
public class Users
{
[FirestoreProperty]
public string id { get; set; }
[FirestoreProperty]
public int age { get; set; }
[FirestoreProperty]
public string birthday { get; set; }
[FirestoreProperty]
public string name { get; set; }
[FirestoreProperty]
public string[] ImageUrl { get; set; }
public List<string> imagepath { get; set; }
}
}
and my xaml file for the GUI
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DockPanel Background="GhostWhite">
<StackPanel Orientation="Horizontal" >
<ListView Name="listview1" ItemsSource="{Binding imagepath}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding imagepath}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Grid Margin="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="Test123" Text="ID: " FontWeight="Bold" />
<TextBlock Text="{Binding id}" Grid.Column="1" />
<TextBlock Text="Name: " FontWeight="Bold" Grid.Row="1" />
<TextBlock Text="{Binding name}" Grid.Column="1" Grid.Row="1" />
<TextBlock Text="Birthday: " FontWeight="Bold" Grid.Row="2" />
<TextBlock Text="{Binding birthday, StringFormat=d}" Grid.Column="1" Grid.Row="2" />
</Grid>
</DockPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
What do I do wrong or is it not possible in general?
Possible duplications
ListView RowDetailsTemplate
I've tried to create a list and append the list to the user object
If the imagepath property contains valid image URIs, this should work:
<ListView Name="tt" ItemsSource="{Binding imagepath}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As stated in a comment by #ASh, you should also create one List<string> per Users object in your code:
async void getAllData()
{
Query docref = database.Collection("users");
QuerySnapshot snap = await docref.GetSnapshotAsync();
foreach (DocumentSnapshot docsnap in snap)
{
Users Employee = docsnap.ConvertTo<Users>();
List<string> AuthorList = new List<string>();
Employee.imagepath = AuthorList;
if (docsnap.Exists)
{
string UrlLinks = "";
for (int i = 0; i < Employee.ImageUrl.Length; i++)
{
string URLS = Employee.ImageUrl[i].ToString();
UrlLinks += URLS + Environment.NewLine;
AuthorList.Add(URLS);
}
// Every URL links get printed out MessageBox.Show(UrlLinks);
DataGridXAML.Items.Add(Employee);
}
}
}
I am trying to build a diary app that uses hub as user interface and update the UI after saving the diary. I already implemented the INotifyPropertyChanged but it didn't work. I want the item that is added after saving to appear on the hub immediately.
public class SampleDataGroup : INotifyPropertyChanged
{
public SampleDataGroup()
{
UniqueId = string.Empty;
Title = string.Empty;
Subtitle = string.Empty;
Description = string.Empty;
ImagePath = string.Empty;
Items = new ObservableCollection<DiaryData>();
}
public string UniqueId { get; private set; }
public string Title { get; private set; }
public string Subtitle { get; private set; }
public string Description { get; private set; }
public string ImagePath { get; private set; }
private ObservableCollection<DiaryData> _items;
public ObservableCollection<DiaryData> Items { get{return _items;} private set
{
OnPropertyChanged("Items");
_items = value;
} }
public override string ToString()
{
if (this.Title != null)
{
return this.Title;
}
else
{
System.Diagnostics.Debug.WriteLine("this is null at tostring");
return null;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public sealed class SampleDataSource : INotifyPropertyChanged
{
private static SampleDataSource _sampleDataSource = new SampleDataSource();
private ObservableCollection<SampleDataGroup> _groups = new ObservableCollection<SampleDataGroup>();
public ObservableCollection<SampleDataGroup> Groups
{
get { return this._groups; }
set { }
}
public static async Task<IEnumerable<SampleDataGroup>> GetGroupsAsync()
{
await _sampleDataSource.GetSampleDataAsync();
return _sampleDataSource.Groups;
}
public static async Task<SampleDataGroup> GetGroupAsync(string uniqueId)
{
System.Diagnostics.Debug.WriteLine("GetGroupAsync is entered phase 1");
await _sampleDataSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
System.Diagnostics.Debug.WriteLine("GetGroupAsync is entered phase 2");
var matches = _sampleDataSource.Groups.Where((group) => group.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
return null;
}
public static async Task<DiaryData> GetItemAsync(string uniqueId)
{
await _sampleDataSource.GetSampleDataAsync();
System.Diagnostics.Debug.WriteLine("GetItemAsync is entered");
// Simple linear search is acceptable for small data sets
var matches = _sampleDataSource.Groups.SelectMany(group => group.Items).Where((item) => item.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
else return null;
}
private async Task GetSampleDataAsync()
{
System.Diagnostics.Debug.WriteLine("GetSampleDataAsync is entered");
//if (this._groups.Count != 0)return;
Uri dataUri = new Uri("ms-appdata:///local/data.json");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(dataUri);
string jsonText = await FileIO.ReadTextAsync(file);
JsonArray jsonArray = JsonArray.Parse(jsonText);
SampleDataGroup group = new SampleDataGroup();
foreach (JsonValue itemValue in jsonArray)
{
JsonObject itemObject = itemValue.GetObject();
group.Items.Add(new DiaryData(itemObject["Title"].GetString(),
itemObject["Content"].GetString(),
itemObject["Coordinate"].GetString(),
itemObject["UniqueId"].GetString(),
itemObject["ImagePath"].GetString(),
itemObject["VideoPath"].GetString()));
System.Diagnostics.Debug.WriteLine(itemObject["Title"].GetString());
}
this.Groups.Add(group);
System.Diagnostics.Debug.WriteLine("GetSampleDataAsync is finished");
}
//}
public event PropertyChangedEventHandler PropertyChanged;
}
here's my XAML File
<Page
x:Class="DiaryAppHub.HubPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DiaryAppHub"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:DiaryAppHub.Data"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
d:DataContext="{Binding Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:data.json}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="HubSectionHeaderTemplate">
<TextBlock Margin="0,0,0,-9.5" Text="{Binding}"/>
</DataTemplate>
<!-- Grid-appropriate item template as seen in section 2 -->
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<Grid Margin="0,0,9.5,9.5" Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="138.5" Width="138.5"/>
<TextBlock Text="{Binding Title}" VerticalAlignment="Bottom" Margin="9.5,0,0,6.5" Style="{ThemeResource BaseTextBlockStyle}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardTripleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Description}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" Foreground="{ThemeResource PhoneMidBrush}" />
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardDoubleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" Foreground="Black"/>
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" Foreground="DimGray"/>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar Background="Transparent">
<AppBarButton Icon="Add" Label="Add" Click="add_onclick"/>
<AppBarButton Icon="Add" Label="Shake it!" />
</CommandBar>
</Page.BottomAppBar>
<Grid x:Name="LayoutRoot">
<Hub x:Name="Hub" x:Uid="Hub" Header="diary app hub" Margin="0,0,0,-59" Foreground="DimGray">
<Hub.Background>
<ImageBrush ImageSource="ms-appx:/Assets/desk_paper.png" Stretch="None"/>
</Hub.Background>
<!--<HubSection x:Uid="HubSection1" Header="SECTION 1" DataContext="{Binding Groups}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView
ItemsSource="{Binding}"
IsItemClickEnabled="True"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,27.5">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>-->
<HubSection x:Uid="HubSection5" Header="Recent"
DataContext="{Binding Groups[0]}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView
AutomationProperties.AutomationId="ItemListViewSection5"
AutomationProperties.Name="Items In Group"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource StandardDoubleLineItemTemplate}"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
</DataTemplate>
</HubSection>
<HubSection x:Uid="HubSection2" Header="All notes" Width ="Auto"
DataContext="{Binding Groups[0]}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}" Height="659" >
<DataTemplate>
<GridView
Margin="0,9.5,0,0"
ItemsSource="{Binding Items}"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Items In Group"
ItemTemplate="{StaticResource Standard200x180TileItemTemplate}"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</DataTemplate>
</HubSection>
</Hub>
</Grid>
You need to raise the PropertyChanged event for the model's properties. The UI doesn't get notified as properties like Title,Subtitle don't raise the PropertyChanged event when they are modified. It should be like this:
private string _title;
public string Title
{
get
{
return _title;
}
set
{
if(_title!=value)
{
_title=value;
OnPropertyChanged("Title");
}
}
}
Do this similarly for other properties. Also, you don't need to raise the PropertyChanged event for an ObservableCollection as an ObservableCollection implements INotifyPropertyChanged by default.
I have a ListView with a list of comments:
<ListView ItemsSource="{Binding Comments}">
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="{Binding User, Converter={StaticResource UsernameToBackgroundColorConverter}}"
Margin="0,5"
HorizontalAlignment="Stretch"
FlyoutBase.AttachedFlyout="{StaticResource FlyoutBase1}"
Holding="BorderCommento_Holding">
<StackPanel>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding User}"
FontSize="20"
Grid.Column="0"
FontWeight="Bold"
Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"/>
<TextBlock HorizontalAlignment="Right"
Text="{Binding DateTime}"
FontSize="20"
Grid.Column="1"
Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"/>
</Grid>
<TextBlock Margin="5,0,5,5"
Text="{Binding Text}"
FontSize="20"
TextWrapping="Wrap"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Comment class:
public class Comment
{
public Comment(String id, String user, String text, String date_time)
{
this.Id = id;
this.User = user;
this.Text = text;
this.DateTime = date_time;
}
public string Id { get; private set; }
public string User { get; private set; }
public string Text { get; private set; }
public string DateTime { get; private set; }
}
The Flyout menu that appears when holding on a comment is defined in Page.Resources:
<Page.Resources>
<MenuFlyout x:Name="flyout1" x:Key="FlyoutBase1">
<MenuFlyoutItem x:Name="ReportCommentFlyout"
Text="{Binding User, Converter={StaticResource ReportOrDeleteComment}}"
Click="ReportCommentFlyout_Click"/>
</MenuFlyout>
</Page.Resources>
Now, in the ReportCommentFlyout_Click I need to know the comment Id that is being reported/deleted.
How can I do it?
I've tried
string CommentId = ((Comment)e.OriginalSource).Id;
But the app crashes...
Your app crashes because you cast e.OriginalSource to Comment and it doesn't work because it's not of that type. Usually, it's often safer to way to do this by using "as"
var comment = someObject as Comment;
if (comment != null)
{
....
}
Regarding your problem, have you tried
var menuFlyoutItem = sender as MenuFlyoutItem;
if (menuFlyoutItem != null)
{
var comment = menuFlyoutItem.DataContext as Comment;
if (comment != null)
{
string CommentId = comment.Id;
}
}
My ListBox control is working fine, except that the data to be bound is not displaying.
My XAML:
<ListBox x:Name="listFileNames" SelectionMode="Single" Margin="10">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Margin="5" Source="{Binding Path=Image}" Stretch="Fill" Width="50" Height="50"></Image>
<StackPanel Grid.Column="1" Margin="5">
<TextBlock Text="{Binding Path=FileName}" FontWeight="Bold"></TextBlock>
<TextBlock Text="{Binding Path=State}"></TextBlock>
<TextBlock Text="This text shows..."></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My code:
public struct StyleDocumentFile
{
public string Image;
public string FileName;
public string State;
}
// ......
StyleDocumentFile sdf = new StyleDocumentFile()
{
Image = "/Images/Loading.png",
FileName = "abc",
State = "Extracting Data...",
};
this.listFileNames.Items.Add(sdf);
Change fields to Property. After this, all works fine.
public struct StyleDocumentFile
{
public string Image { get; set; }
public string FileName { get; set; }
public string State { get; set; }
}
You should set ItemsSource in ListBox definition like ItemsSource="{Binding Model.Items}". In addition you must call RaisePropertyChanged in setter of model properties.
First time using a ListBox and after following this , I'm having issues having data actually display. The ListBox is just empty and white with no text in it.
I made a separate textbox to test an individual "Tweet" object out and it is indeed outputting what I want it to. I think my issue either lies in XAML or Tweets. But nothing looks out of place.
Tracing reveals that Tweets successfully adds a proper Tweet object with what I need. But my ListBox Count is always 0.
<Grid Opacity="0.8">
<Grid.Resources>
<local:Tweets x:Key="tweets"/>
</Grid.Resources>
<Rectangle Fill="Gray" Margin="1523,0,0,729" Height="321" Width="389">
<Rectangle.Effect>
<DropShadowEffect/>
</Rectangle.Effect></Rectangle>
<ListBox ItemsSource="{StaticResource tweets}" Height="321" Margin="340,40,1096,0" x:Name="twitterBox" VerticalAlignment="Top" Width="476">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
In my cs:
public class Tweet
{
public String imgSrc { get; set; }
public String user { get; set; }
public String tweet { get; set; }
public Tweet(String user, String tweet, String img)
{
this.imgSrc = img;
this.user = user;
this.tweet = tweet;
}
}
public class Tweets : ObservableCollection<Tweet>
{
public Tweets()
{
}
public void addTweet(Tweet tweet)
{
Add(tweet);
}
}
public void SomeFunction()
{
Tweets myTwitter = new Tweets();
myTwitter.addTweet(new Tweet(tweet.User.ScreenName, tweet.Text, tweet.User.ProfileImageUrl));
}
ItemTemplate code is ok but you should remove this Margin="1523,0,0,729".
ListBox is empty because items source is empty. You should add some items.
To add items in XAML you should add default constructor to Tweet class.
public class Tweet
{
public String imgSrc { get; set; }
public String user { get; set; }
public String tweet { get; set; }
public Tweet(){}
public Tweet(String user, String tweet, String img)
{
this.imgSrc = img;
this.user = user;
this.tweet = tweet;
}
}
And now you can write something like this:
...
<Grid.Resources>
<local:Tweets x:Key="tweets">
<local:Tweet imgSrc="imgSrc1" user="user1" tweet="tweet1" />
<local:Tweet imgSrc="imgSrc2" user="user2" tweet="tweet2" />
</local:Tweets>
</Grid.Resources>
...
Result:
Add items in code-behind.
To do that you should use function: FindResource (msdn).
XAML:
<Grid Name="mainGrid" Opacity="0.8">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Resources>
<local:Tweets x:Key="tweets">
<local:Tweet imgSrc="imgSrc1" user="user1" tweet="tweet1" />
<local:Tweet imgSrc="imgSrc2" user="user2" tweet="tweet2" />
</local:Tweets>
</Grid.Resources>
<Button Content="Add new item" Click="Button_Click" />
<ListBox x:Name="twitterBox" ItemsSource="{StaticResource tweets}"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
var coll = mainGrid.FindResource("tweets") as Tweets;
if (coll != null)
{
coll.Add(new Tweet("user", "name", "url"));
}
}
Second solution:
The better solution will be if you will create an instance of class Tweets in code behind.
XAML:
<Grid Name="mainGrid" Opacity="0.8">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Add new item" Click="Button_Click" />
<ListBox x:Name="twitterBox" ItemsSource="{Binding tweets}"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
tweets = new Tweets();
tweets.Add(new Tweet("user1", "name1", "url1"));
tweets.Add(new Tweet("user2", "name2", "url2"));
tweets.Add(new Tweet("user3", "name3", "url3"));
this.DataContext = this;
}
public Tweets tweets { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
tweets.Add(new Tweet("user4", "name4", "url4"));
}
}
You add tweets to a different collection than the one displayed by the listbox.