Binding list collection to a list view - c#

I have a list of objects and I would like to bind them to a list view with the use of data templates (one way binding). Currently, the list view does not show anything and I am new to this and I do not have any idea where the issue lies.
This is my object class
public class CategoryObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string CategoryObjectInstance;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is my XAML Code for the list view
<ListView Grid.Row="2" Name="ListView1" Margin="10,0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
Style="{StaticResource ListViewItemContentTextBlockStyle}"
Text="{Binding Path=CategoryObject.CategoryObjectInstance, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is my code to input the itemssource
protected override void OnNavigatedTo(NavigationEventArgs e)
{
List<CategoryClass.CategoryObject> newCategoryObjectList = new List<CategoryClass.CategoryObject>();
for (int i = 0; i < 10; i++)
{
CategoryClass.CategoryObject newCategoryObject = new CategoryClass.CategoryObject();
//set arbitarily value
newCategoryObject.CategoryObjectInstance = i.ToString() + i.ToString() + i.ToString() + i.ToString() + i.ToString();
newCategoryObjectList.Add(newCategoryObject);
}
//to debug
var messageDialog = new Windows.UI.Popups.MessageDialog(newCategoryObjectList.Count.ToString());
messageDialog.ShowAsync();
ListView1.ItemsSource = newCategoryObjectList;
}
What am I doing wrong here? In addition, how/can data context be used to accomplished this? thanks so much in advance!

There are couple of problems here:
the first main problem is that you have the wrong path in binding in XAML, it should be:
<TextBlock Text="{Binding Path=CategoryObjectInstance, Mode=OneWay}" />
the second main problem is that you should bind to property - without this it also won't work
and you are defining OnPropertyCanged but you are not using it (this is not so important now, but further changes won't dispaly in the List). Taking into account this point and above one, it should look like this:
public string categoryObjectInstance;
public string CategoryObjectInstance
{
get { return categoryObjectInstance; }
set { categoryObjectInstance = value; OnPropertyChanged("CategoryObjectInstance"); }
}
but this above will only help once 'CategoryObjectInstance' changes, it won't help you when you add/remove items later, I would advise to use ObservableCollection instead of a List
Also I see that you use MessageBox for debugging which is not suitable for this, try to use:
//to debug
Debug.WriteLine(newCategoryObjectList.Count);
Open Debug->Output window in Visual Studio - you will see it there. If you were using it before, you could have seen your previous problems.

DataContext already set to the object in the list. Try this:
Text="{Binding CategoryObjectInstance, Mode=OneWay}" />

Related

How to get access to ToggleSwitch instance within ListView in C# UWP?

I opened the question here but we cannot come to the solution for my problem. I decided to create new question as we came to some assumptions and the former question does not refer to the real problem(we thought it is the problem with binding but as you will read on it is not).
In few words I have a ListView with data from list called jointList.
The list is doing well and it has all the data necessary. (I checked it)
On each row of the ListView I put a ToggleSwitch(in xaml) and then I try to do something with each of the switches.
Each switch should correspond to the data from the same row.
I created Toggled event that should apply to all toggleSwitches like this:
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
foreach (var product in jointList)
{
if (product.IsOn == true)
{
ToggleTest.Text = product.ProductId.ToString(); // this is for testing only, later I would do something with the data retrieved
ToggleTest.Visibility = Visibility.Visible;
}
else
{
ToggleTest.Visibility = Visibility.Collapsed;
}
}
}
But this is making only one toggleSwitch work. It's the switch that corresponds to the last added product to the list ( I am guessing that it is refering to the last Id). The other switches return nothing as if the method was not iterating through the list correctly or as if there was only one switch hooked up.
So, is it possible to get all switches up and running by using just one Toggled event as I attempt to do?
Here's a sample which shows one way.
In this example we have the following Product view model:
public class Product : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
So just a single Name-property.
Then we have MainPage where we create a collection of products:
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var items = new ObservableCollection<Product>();
for (int i = 0; i < 9; i++)
{
items.Add(new Product($"item {i}"));
}
this.Items.ItemsSource = items;
}
And the XAML which creates the view:
<ListView Loaded="FrameworkElement_OnLoaded" x:Name="Items">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="RowContent" Text="{Binding Name}"/>
<ToggleSwitch x:Name="Toggle" Grid.Column="1" Toggled="Toggle_OnToggled"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The result:
Now we want to change the text when user toggles the switch. This is done in Toggle_OnToggled-event handler:
private void Toggle_OnToggled(object sender, RoutedEventArgs e)
{
var toggle = (ToggleSwitch) sender;
var dataContext = ((Grid)toggle.Parent).DataContext;
var dataItem = (Product) dataContext;
dataItem.Name = $"Toggled {toggle.IsOn}";
}
So after a few toggles:
Mikael Koskinen has delivered the answer to my problem.
Most of my code was correct and identical to his solution, apart from the last bit that is OnToggled event handler.
Here is the working andd correct handler:
private void Toggle_OnToggled(object sender, RoutedEventArgs e)
{
var toggle = (ToggleSwitch)sender;
var dataContext = ((Grid)toggle.Parent).DataContext;
var dataItem = (ScheduleList)dataContext;
ToggleTest.Text = dataItem.ProductId;
}
My previous version of handler didn't include the important bit, that is dataContext and dataItem.
It works like a charm now.

ListBox filled with binding doesn't select item on click

I'm trying to use a ListBox to choose an entry and then display a picture belonging to this selected entry. But just at the beginning I got my first problem: filling the ListBox with binding is working, but if I click on one line in my running program, it doesn't select the line. I can just see the highlighted hover effect, but not select a line. Any ideas what my mistake could be?
This is my XAML:
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontSize="24"/>
And in MainWindow.xaml.cs I'm filling the ListBox with entries:
private void fillEntrySelectionListBox()
{
//Fill listBox with entries for active user
DataContext = this;
entryItems = new ObservableCollection<ComboBoxItem>();
foreach (HistoryEntry h in activeUser.History)
{
var cbItem = new ComboBoxItem();
cbItem.Content = h.toString();
entryItems.Add(cbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + activeUser.Id;
//show image matching the selected entry
if (activeUser.History != null)
{
int index = entrySelection.SelectedIndex;
if (index != -1 && index < activeUser.History.Count)
{
this.entryImage.Source = activeUser.History[index].Image;
}
}
}
So I can see my ListBox correctly filled, but not select anything - so I can't go on with loading the picture matching the selected entry.
I'm still quite new to programming, so any help would be great :)
EDIT: If someone takes a look at this thread later: here's the - quite obvious -solution
XAML now looks like this
<ListBox x:Name="entrySelection" ItemsSource="{Binding Path=entryItems}" HorizontalAlignment="Left" Height="335" Margin="428,349,0,0" VerticalAlignment="Top" Width="540" FontFamily="Siemens sans" FontSize="24">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind to fill it:
//Fill listbox with entries for selected user
DataContext = this;
entryItems = new ObservableCollection<DataItem>();
foreach (HistoryEntry h in selectedUser.History)
{
var lbItem = new DataItem(h.toString());
entryItems.Add(lbItem);
}
this.entrySelection.ItemsSource = entryItems;
labelEntrySelection.Text = "Einträge für: " + selectedUser.Id;
And new Class DataItem:
class DataItem
{
private String text;
public DataItem(String s)
{
text = s;
}
public String Text
{
get
{
return text;
}
}
}
You are filling it with ComboBoxItem, which is not relevant to the ListBox, and also wrong by definition.
You need to have the ObservableCollection filled with data items.
Meaning, make a class that contains the data you want to store, and the ListBox will generate a ListBoxItem automatically per data item.
http://www.wpf-tutorial.com/list-controls/listbox-control/

WPF Binding Image Source

maybe stupid question, but I don't know anymore...
I have ViewModel class like this:
public class MainWindowsViewModel : INotifyPropertyChanged
{
private ImageSource _img;
public ImageSource StatusImage
{
get { return _img; }
set
{
_img = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Binding in XAML looks like this:
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
<Image x:Name="gui_image_status" HorizontalAlignment="Left" Height="26" Margin="144,10,0,0" VerticalAlignment="Top" Width="29" Source="{Binding Path=StatusImage}" />
And I set content of ImageSource like this:
MainWindowsViewModel _view = new MainWindowsViewModel();
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
_view.StatusImage = yourImage;
But it does not work. I think that problem is in that NotifyPropertyChanged, because I tried place brake point in the set and get. Get triggered few times at the start, after then set triggered as well with correct ImageSource, but after then get did not triggered anymore. Like no setting ever happened.
It's really simply binding that I have done many times similarly...I don't know why it doesn't work this time.
You are creating two instances of your MainWindowsViewModel class, one in XAML by
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
and one in code behind by
MainWindowsViewModel _view = new MainWindowsViewModel();
So your code behind sets the property on a different view model instance than the one the view is bound to.
Change your code behind to this:
var viewModel = (MainWindowsViewModel)DataContext;
viewModel.StatusImage = new BitmapImage(...);
I didn't find any problems in your code, but you can try to check few things.
Check that your Image added to the project and set build action of images to Content (copy if newer).
Before updating ImageSource call Freeze method to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
yourImage.Freeze();
_view.StatusImage = yourImage;
Also, there is an easier way to bind image in WPF. You can use string as a source and set a resource path to the binded property:
public string StatusImage
{
get { return "/AssemblyName;component/Sources/red.png"; }
}

C# visible doesn't works

I'm making a Universal application for Windows Phone 8.1 and have a problem with my code.
After TextBlock value become greater or equal than 22, some images should become visible. If the value is less than 22 all images should be invisible.
My question: How I can get visible images after textblock value >="22"
This is my code to hide images:
private void points_Loaded(object sender, RoutedEventArgs e)
{
int n = 0;
bool b = int.TryParse(points.Text, out n);
DataContext = this;
ImageVis = (b && n >= 22) ? Visibility.Visible : isibility.Collapsed;
}
private Visibility imageVis;
public Visibility ImageVis
{
get { return imageVis; }
set
{
imageVis = value;
RaisePropertyChanged("ImageVis");
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
This code part is from XAML:
<Image x:Name="hole17img"
HorizontalAlignment="Left"
Height="57"
Margin="10,3540,0,0"
VerticalAlignment="Top"
Width="380"
Source="Assets/septinpatsmit.png"
Stretch="Fill"
Visibility="{Binding ImageVis, Mode=TwoWay}"/>
I have problem with: RaisePropertyChanged("ImageVis");
The name 'RaisePropertyChanged' does not exist in the current context
Does this mean I have make some object with that name? or something else?
I can provide my My application so you can see what's happening.
My application sample
RaisePropertyChanged is MVVM Light's method and makes UI updated whenever you raise a property with the given name.In the XAML code behind , you bind ViewModel's properties to XAML properties and when RaisePropertyChanged triggers , it notifies the given property and UI is refreshed after.
You also need to use Converters to convert boolean to Visibility.In general , you need more MVVM Pattern knowledge to Windows projects.
Check out this post
http://www.mvvmlight.net/doc/

showing multiple pushpins on a map in windows phone using mvvm model

I have the following view in my mvvm model based app which should display all the pushpins I bind to it using binding property "PushPinLocation" from my view model.
<MapNS:Map
Center="{Binding MapCenter, Mode=TwoWay}" Margin="0,0,-5,0"
CartographicMode="{Binding MapMode, Mode=TwoWay}"
LandmarksEnabled="True" PedestrianFeaturesEnabled="True"
ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}"
Foreground="AliceBlue" Grid.Row="1" Height="713" Width="425"
x:Name="mapPanoramaAddress" >
<!--Adding Location to show map initially until the data arrived-->
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="StoresMapItemsControl" >
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin x:Name="PushPins" Background="White"
GeoCoordinate="{Binding PushPinLocation}"
Content="{Binding PushPinDisplayText}"
Visibility="Visible" />
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>
<maptk:UserLocationMarker GeoCoordinate="{Binding PushPinLocation}" x:Name="UserLocationMarker" Visibility="Visible" />
</maptk:MapExtensions.Children>
</MapNS:Map>
In the geolocator positionchanged event which triggers for every few meters I am setting the value for binding property "PushPinLocation" (from my view model) which is common for pushpin and location marker.
//PushPinLocation
private GeoCoordinate _PushPinLocation = new GeoCoordinate(40.712923, -74.013292); //cannot assign null
public GeoCoordinate PushPinLocation
{
get { return _PushPinLocation; }
set
{
if (_PushPinLocation != value)
{
_PushPinLocation = value;
RaisePropertyChanged("PushPinLocation");
}
}
}
in the same viewmodel geolocator_Position changed event I am setting the pushpinlocation:
private void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
this.PushPinLocation = args.Position.Coordinate.ToGeoCoordinate();
}
But I always see the latest one showing up and old ones are never shown on the map.Is there any way I can retain the old ones as well.
This post is a year old, but unanswered, so here is my answer:
Instead of binding to a single PushPinLocation, use a collection. In your ViewModel, add this:
private List<GeoCoordinate> _pushPinLocations;
public List<GeoCoordinate> PushPinLocations
{
get { return _pushPinLocations; }
set
{
_pushPinLocations = value;
OnPropertyChanged("PushPinLocations");
}
}
and change your event to:
private void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
this.PushPinLocations.Add(args.Position.Coordinate.ToGeoCoordinate());
}
That will add the new location to the list and as long as its bound to this list of locations, all pins will show.
<maptk:MapItemsControl Name="StoresMapItemsControl" >
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin x:Name="PushPins" Background="White"
GeoCoordinate="{Binding PushPinLocations}"
Content="{Binding PushPinDisplayText}"
Visibility="Visible" />
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>

Categories