I'm very new to programming in Visual Studio and for Windows Phone 8. I do have knowledge in PHP, so I understand the basics of syntax, albeit a slight differentiation from C#, however.
Anyways, I am simply trying to make a list I've declared in a variable in \MainPage.xaml.cs viewable in \MainPage.xaml, via binding, I guess that's how it's done.
I'm trying to make it as basic as possible for now; here is what I have in the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using lbpme_viewer.Resources;
namespace lbpme_viewer
{
public partial class MainPage : PhoneApplicationPage
{
List<String> MenuItems = new List<String> { "portal", "news", "myprofile", "settings" };
// Constructor
public MainPage()
{
InitializeComponent();
}
}
}
And I would just make 4 plain textblocks, but I want to remove "myprofile" based on the user's settings, but before I get to that, right now I just declared them all in one list of strings.
And a portion of my XAML file:
<!--Panorama item one-->
<phone:PanoramaItem Header="first item">
<!--Single line list with text wrapping-->
<phone:LongListSelector x:Name="menuList" Margin="0,0,-22,0" ItemsSource="{Binding Path=MenuItems}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,-6,0,12">
<TextBlock Text="{Binding}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PanoramaItem>
And obviously the items won't show in the LongListSelector. I don't expect them to because I know I'm missing one or many pieces of code required to do so, and my question is, how?
Again, I just want the list of phrases in the MenuList variable to appear in my LongListSelector, like how "design one" "design two" does in the default panorama/pivot app upon creation.
And if it wouldn't be much different, how would I get any variable from the C# code to appear in the XAML file?
Thank you!
Try binding to an ObservableCollection<string> that implements INotifyPropertyChanged like this
List<String> MenuItemsList = new List<String> { "portal", "news", "myprofile", "settings" };
private ObservableCollection<string> _menuItems;
public ObservableCollection<string> MenuItems
{
get { return _menuItems; }
set
{
if (_menuItems == value) return;
_menuItems = value;
NotifyPropertyChanged(); // or NotifyPropertyChanged("MenuItems");
}
}
then in your constructor new up the ObservableCollection by passing in the List
MenuItems = new ObservableCollection<string>(MenuItemsList);
You also need this in your class (from http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx)
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Take a look at MVVM Light, it makes a lot of WPF things easier. With MVVM Light, your property could look like this (and you wouldn't need all the NotifyPropertyChanged stuff)
private ObservableCollection<string> _menuItems;
public ObservableCollection<string> MenuItems
{
get { return _menuItems; }
set
{
if (_menuItems == value) return;
_menuItems = value;
RaisePropertyChanged(() => MenuItems);
}
}
Related
I have a panel and my idea is to have it populated by a stack panel containing two text boxes. When the user enters something in the left box, something should be generated in the right one, as follows.
<StackPanel Orientation="Horizontal">
<TextBox Name="Lefty" LostFocus="FillMyBuddy" />
<TextBox Name="Righty" LostFocus="FillMyBuddy" />
</StackPanel>
However, I'd like to add an option to add/remove rows and, since I wish not to limit myself to the number of such, I get a bit uncertain regarding the approach on two points.
Manipulating DOM (well, it's XAML/WPF but you see what I'm aiming at).
Event handling.
Is it a big no-no to programmatically affect the mark-up structure of the window? Or is it OK to add/remove panels during run-time?
What would the recommended way to be if I want the Lefty number 3 change stuff in Righty number 3? Anything more neat than checking the sender and pulling its siblings from the parent? I want to use a single event handler for any and all rows (knowing that the operations are always intra-row-wise).
You will want to follow MVVM, and have no code in your code-behind (programmatically affect the mark-up structure) files. The concept is easy when you grasp it, so learn it before you start writing your code.
In short, you are going to want to have a view model (something that implements INotifyPropertyChanged (INPC)) which holds your collection of items (which are going to be models, or view models in pure-MVVM). In "hybrid"-MVVM you could just have your models implement INPC.
Then, through the use of commands, you'd implement the logic to remove items from the list that its in. You can pass references, raise notification, using event bubbling, etc. (it's your preference) to have the item actually removed. In my case, I just passed a "manager" to the hybrid-model and held a reference to that. When the command is called (button is clicked), the model calls for the reference to remove itself from the list.
After you do that you define a DataTemplate to define what an "item" should look like one the View. You use a ItemsControl to show a collection of items, and bind to its ItemsSource so the collection of items are shown. Set your ItemsControl.ItemTemplate to the DataTemplate you created, and anything added to the collection bound to ItemsSource of the type defined in DataTemplate.DataType will render as you specify in the DataTemplate.
At the end of the day, you should learn about MVVM design, DataContext, INPC, Commands, Control types and their "main" properties, e.g. everything that inherits from ItemsControl has an ItemsSource property.
Here is a working example, where changing the original string, will reverse it and put it in the read-only right side text box:
MainWindow.xaml.cs (code-behind)
public partial class MainWindow : Window
{
StructureVm _struct = new StructureVm("Test");
public MainWindow()
{
InitializeComponent();
DataContext = _struct;
}
}
MainWindow.xaml (View)
<Window x:Class="DataTemplateWithCommands.MainWindow"
xmlns:local="clr-namespace:DataTemplateWithCommands"
Title="MainWindow" Height="350" Width="525" Background="Orange">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Model}"
x:Key="VmItem">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Original, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Encoded}"
IsReadOnly="True" />
<Button Content="X"
Command="{Binding RemoveMeCommand}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource VmItem}">
</ItemsControl>
</Grid>
</Window>
Interface (helpful for Dependency Injection)
public interface IStructureManager
{
bool RemoveItem(Model itemToRemove);
}
ViewModel
public class StructureVm : IStructureManager
{
private readonly ObservableCollection<Model> _items;
private readonly string _title;
public StructureVm(string title)
{
_title = title;
_items = new ObservableCollection<Model>
{
new Model(this, "12"),
new Model(this, "23"),
new Model(this, "34"),
new Model(this, "45"),
new Model(this, "56"),
new Model(this, "67"),
new Model(this, "78"),
new Model(this, "89"),
};
}}
public ObservableCollection<Model> Items
{
get
{
return _items;
}
}
public string Title
{
get
{
return _title;
}
}
public bool RemoveItem(Model itemToRemove)
{
return _items.Remove(itemToRemove);
}
}
Model (not pure-MVVM, pure MVVM models don't implement INPC, and don't have Command in them)
public class Model : INotifyPropertyChanged
{
private readonly RelayCommand _removeMe;
private string _original;
private string _encoded;
private readonly IStructureManager _manager;
public string Original
{
get
{
return _original;
}
set
{
_original = value;
Encoded = ReverseString(_original);
NotifyPropertyChanged();
}
}
public string Encoded
{
get
{
return _encoded;
}
set
{
_encoded = value;
NotifyPropertyChanged();
}
}
public ICommand RemoveMeCommand
{
get
{
return _removeMe;
}
}
public Model(IStructureManager manager, string original)
{
Original = original;
_manager = manager;
_removeMe = new RelayCommand(param => RemoveMe(), param => CanRemoveMe);
}
private void RemoveMe()
{
_manager.RemoveItem(this);
}
private bool CanRemoveMe
{
get
{
//Logic to enable/disable button
return true;
}
}
private string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
RelayCommand implementation
From here on out all you have to do is change the attributes of your controls to whatever you're happy with and call it good. The example might be ugly, but I'm leaving it as an exercise for you to figure out other properties/attributes of WPF controls.
I have a feeling this is a bug in wpf. Let me know what you guys think of this.
To keep everything simple I made demo example in .net 4.0
I have a ContentControl with Content bound to Data and ContentTemplate which holds a CheckBox bound to Content.
The problem is Ok property is never true no matter how often I click the CheckBox.
As if CheckBox doesn't pass the new value to ViewModel.
Take a look at this:
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
This is my ViewModel
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
}
public class ViewModel : INotifyPropertyChanged
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; this.OnPropertyChanged("Txt"); }
}
private bool ok;
public bool Ok
{
get { return ok; }
set { ok = value; this.OnPropertyChanged("Ok");}
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Any ideas how to fix this issue with ContentTemplate?
Your problem is a common problem related to the use of value types. You're data binding your checkbox to a primitive value type (bool) (which is a pretty uncommon thing to do). Since Binding.Source is of type object, your boolean is getting boxed into an object. Any updates on that boxed object has no effect on the original property on ViewModel.
You can test this theory by replacing that boolean with a struct like this:
public struct MyStruct
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public ViewModel Parent { get; set; }
}
and change your binding:
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
If you put breakpoints on the IsChecked getter and setter, you will see that the binding works. However, when you hit one of the break points, try to investigate this value in the Immediate Window:
? this.Parent.Ok.IsChecked
You should see that the MyStruct property on the parent view model is not being affected by the databinding at all.
Full test code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
vm.Ok = new MyStruct { Parent = vm, IsChecked = false };
this.DataContext = vm;
}
}
public class ViewModel : INotifyPropertyChanged
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; this.OnPropertyChanged("Txt"); }
}
private MyStruct ok;
public MyStruct Ok
{
get { return ok; }
set { ok = value; this.OnPropertyChanged("Ok"); }
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public struct MyStruct
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public ViewModel Parent { get; set; }
}
}
xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
</Window>
you can do this way
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=Ok, Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
in above example I bind the Content to the view model itself and then IsChecked of CheckBox to the Ok property.
The Problem is that ContentControl won't pass it's DataContext to the Content.
You need to set the DataContext manually.
<CheckBox DataContext="{Binding DataContext,RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}" IsChecked="{Binding Path=., Mode=TwoWay}"/>
After some more research (and after posting an irrelevant answer and then deleting it again) -- and being motivated by some discussion with Eren Ersönmez and Dev Hedgehog -- the reason of why the two-way binding IsChecked="{Binding Path=., Mode=TwoWay}" cannot work became more clear.
(Side note: This answer does not contain a solution to the problem. Pushpraj's answer already provides a nice solution.)
{Binding Path=., Source=SomeSource} denotes a binding which binds to the binding source directly. An equivalent binding syntax is {Binding Source=SomeSource}.
However, bindings to the binding source itself cannot be two-way. Trying so by specifying {Binding Source=SomeSource, Mode=TwoWay} will cause an InvalidOperationException during run-time.
On the other hand, {Binding Path=., Source=SomeSource, Mode=TwoWay} will not produce an exception - but it still does not enable a two-way binding for such a data binding. Rather, it only fools the binding validation/error handling mechanism of the binding engine.
This has been already found out and discussed in answers to other binding-related questions, such as the answers by Allon Guralnek or by H.B. (and were it not for their answers and my discussion with Eren Ersönmez, i would probably still be clueless of what is going on here...).
This also means that the issue is not caused by a boxed value type being a binding source per se, but rather due to trying to use a two-way binding with the binding path ..
Why does it make no sense to have two-way bindings with binding path .?
Below is a small example demonstrating the issue. It also demonstrates that the problem is not restricted to situations where a boxed value type is being the binding source, nor that it is restricted to bindings involving DataContext.
public class Demo
{
public static string SourceString
{
get { return _ss; }
set { _ss = value; }
}
private static string _ss = "Hello World!";
}
The string (a reference type) provided by static property Demo.SourceString will be used as the binding source in the following XAML snippet:
<TextBox Text="{Binding Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
(Note that the object provided by the My:Demo.SourceString property is the binding source; the property itself is not the binding source.)
This XAML above will produce an InvalidOperationException at run-time.
However, using the equivalent XAML:
<TextBox Text="{Binding Path=., Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
will not produce an exception during run-time, but the binding is nevertheless not a working two-way binding. Text entered into the text box will not be promoted back to the Demo.SourceString property. It can't -- all the binding knows is that it has a string object as source and a binding path which is ..
...but it would just need to update Demo.SourceString, that can't be too difficult, or?
Assume for a moment that a two-way binding with a path . as shown in the XAML above would try to work as a two-way binding - would it result in meaningful behavior? What would happen when the TextBox.Text property has changed its value due to the user inputting text?
The binding would theoretically try to promote the new string value back to the binding source by applying the binding path . onto the object that serves as the binding source (the string "Hello World!"). That does not make much sense... Well, it could perhaps replace the original binding source (the "Hello World!" string) with the string from the TextBox.Text property - but this would be rather meaningless as this change would be local and internal to the binding only. The binding would not be able to assign the new string to Demo.SourceString -- unless someone knows a way of how to obtain a reference to Demo.SourceString by applying the binding path . to a string object containing "Hello World!". Hence, two-way binding mode is not a feasible option for bindings which bind to the binding source itself.
(If somebody is confused about how the example above would apply to a binding using the DataContext like {Binding Path=., Mode=TwoWay}, just ignore the class Demo and substitute any occurences of Demo.SourceString in the text with DataContext.)
Hy,
I have a menu with a few menu items. I have various other elements like a treeview and some controls. When I open the program all elements in the menu are available. But the first step I have to do is to connect to the server. So all the the other elements shouldn't available till there is made a connection via the connection menu item.
Then I want to show only menu items if a special tree view (for instance the whole item structure) item is choosen for instance all topics. For instance there should be special menu items available if I click a treeview entry in the menu.
Is it possible to accomplish this in xaml?
Update1:
MainWindow.xaml
Title="Service Bus Visualizer" Height="680" Width="1200" Name="Root"
<MenuItem Header="_Read File" Name="readFile" Click="MenuItemReadFile" HorizontalAlignment="Right" Width="187" IsEnabled="{Binding Path=DataContext.IsMonitoring, ElementName=Root}">
<MenuItem.Icon>
<Image Source="Icons/Open.ico" Width="16" Height="16" />
</MenuItem.Icon>
</MenuItem>
MainWindow.xaml.cs
public bool IsMonitoring
{
get
{
return isMonitoring;
}
set
{
isMonitoring = value;
RaisePropertyChanged("IsMonitoring");
}
}
private bool isMonitoring;
public MainWindow()
{
InitializeComponent();
this.IsMonitoring = false;
this.DataContext = this;
Application.Current.MainWindow = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
ConnectionWindow.xaml.cs
MainWindow mainWindow = Application.Current.MainWindow as MainWindow;
mainWindow.IsMonitoring = true;
I get no error on the output window but it doesn't work?
Update2:
I have a second parameter which is a ObservableCollection.
MainWindow.xaml
<ListBox Grid.Row="3" Name="Logger" ItemsSource="{Binding Path=DataContext.LoggingList, ElementName=Root}" DisplayMemberPath="Message" IsSynchronizedWithCurrentItem="True" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectionChanged="BringSelectionIntoView">
</ListBox>
MainWindow.xaml.cs
public static ObservableCollection<Log> LoggingList { get; set; }
public MainWindow()
{
LoggingList = new ThreadSafeObservableCollection<Log>();
this.IsMonitoring = false;
this.DataContext = this;
Application.Current.MainWindow = this;
InitializeComponent();
}
Log.cs
public class Log : INotifyPropertyChanged
{
public string Message {
get
{
return message;
}
set
{
message = value;
NotifyPropertyChanged("Message");
}
}
public string message;
public Log()
{
}
public Log(string message)
{
this.Message = message;
NotifyPropertyChanged("Message");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Best regards
First, you have two options as far as "availability" is concerned. The "IsEnabled" property, and the "Visible" property. "IsEnabled" is a bool, and determines if the user can click/select/interact with a given element. Generally speaking, if this property is set to false the element will appear "greyed out".
Changing Visibility will make the element appear/disappear entirely. When set to "Visible" (this is actually an enum), it appears normally. When set to "Hidden", the space for it is reserved on the UI, but you can't actually see it. When set to "Collapsed" you cannot see it and no space is reserved for it in the layout.
For your first requirement (waiting to connect to the server), I would use IsEnabled bound to a "IsConnected" property like so:
IsEnabled="{Binding IsConnected}"
That would go on each item that needs to have this behavior.
The "context-specific" menu items are a bit more complicated, but the basic idea would be a binding on Visible for each of the context sensitive items like:
Visible="{Binding Path=SelectedItem, ElementName=MyTreeView, Converter={StaticResource SelectedItemToVisibilityConverter}, ConverterParameter={x:Type ChildItem}"
I am assuming that each items visibility depends on what type of item is selected (child or parent), you should be able to extend the example if I was wrong. The converter would then look like:
public class SelectedItemToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((parameter as Type).IsInstanceOfType(value))
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(...)
{
return Binding.DoNothing;
}
}
Please let me know if I can clarify anything. Hopefully that gives you a good starting point for what you are trying to do.
Update:
Looking at your code I see a couple potential problems:
IsMonitoring is declared as a public field. Binding only works with public properties. This property needs to raise the PropertyChanged event for it to work.
In "MainWindow.xaml.cs" you are setting the DataContext multiple times. This isn't how DataContext works in WPF. You need to set it to one object (your ViewModel) that contains all the properties you are interested in binding to. While it is considered bad practice, you could write this.DataContext = this to get it working before you build a ViewModel class.
The IsMonitoring field is declared in your "MainWindow.xaml.cs" file. First, this should be in a view model. Second, the binding is looking for that property on the MenuItem class (likely because it is in some sort of ItemsControl). If you want it on the root data context, give your window some name (like "Root") and use the following binding:
"{Binding Path=DataContext.IsMonitoring, ElementName=Root}"
Hopefully that makes sense. Let me know if I can help further!
I have a List Box in WPF and I want the content of the list to change everytime the values are changed in code.
At the start of my program I insert the default values in the ListBox and this works well.
using System; using System.Collections.Generic; using System.Linq;
using System.Text; using System.Threading.Tasks; using System.ComponentModel;
using System.Collections.ObjectModel;
public partial class MainWindow : Window
{
//here is my data which goes into the list
private DataTable _dataTable1 = null;
//list which goes into ListBox
private List<CompareListItem> _compareListItems1 = null;
public MainWindow()
{
InitializeComponent();
// ..Code missing which writes data in _dataTable
//ReloadCompareList fills the _compareListItems1 with data from _dataTable1
_compareListItems1 = ReloadCompareList(_dataTable1);
//here I do the binding to the ListBox
compareSelectionList1.ItemsSource = _compareListItems1;
}
But when I change the values here it does not affect the ListBox
//this method is called when i want to replace the entire _compareListItems1 list
private void tableList1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//this method write my data into _dataTable
_dataTable1 = ReloadTableList(tableList1, _dataTable1, tableGrid1);
if (_dataTable1 != null)
{
//the values of my compare list are replaced, but nothing happens with the ListBox
_compareListItems1 = ReloadCompareList(_dataTable1); // ESSENTIAL LINE
}
}
}
Each item in my ListBox will be a CompareListItem. I found here on stackoverflow the following topic about INotifyPropertyChanged and I implemented this here. It works when i update a single object in my list.
// Class for the items displayed in the Listbox of the compare list
public class CompareListItem : INotifyPropertyChanged
{
private string itemTitle;
public string ItemTitle
{
get{return itemTitle;}
set{
//this works when a single value in the list is changed, but not if i add or delete someting
SetField(ref itemTitle, value, "ItemTitle");
}
}
public CompareListItem(string title) {
//does not affect the data bindings, could be "itemTitle = title;" to
SetField(ref itemTitle, title, "ItemTitle");
}
//this is from https://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
edit: here the XAML of my ListBox:
<ListBox x:Name="compareSelectionList1" Margin="10,0,10,10" IsSynchronizedWithCurrentItem="False" Grid.Row="1" Height="100" VerticalAlignment="Bottom" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<TextBlock Text="{Binding ItemTitle, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I add the following line it will work. But I think that is not the meaning of the approach with the DataBindings. I understood the approach like "you mention the view-element once in the code and then you never use the name "compareSelectionList1" again."
_compareListItems1 = ReloadCompareList(_dataTable1); // ESSENTIAL LINE
compareSelectionList1.ItemsSource = _compareListItems1;
How can I replace my list so that the ListBox will be updated to via the data-binding?
You're setting _compareListItems1 to a new instance of List<CompareListItem>, but compareSelectionList1.ItemsSource still refers to the previous instance. That's why you need to reassign ItemsSource for it to work.
But I think that is not the meaning of the approach with the DataBindings
Currently, you're not using a binding to set the ItemsSource, so it can't be refreshed automatically. To do that, you need to expose the list as a property, and implement INotifyPropertyChanged in your Window (another option is to expose it as a dependency property). In XAML, bind ItemsSource to the list property, and it will work as expected.
I've been trying to use a List<> of a Model.Person that I have inside a ListBox, using a ItemTemplate for style and then retrieving the correct Model.Person from the list when one has been selected. I've looked everywhere for sample code (and tried leading data binding, but still can't get my head around it properly).
List<Model.Person> people = (comes from IsolatedStorageSettings)
On a page I have a ListBox, say named "List".
<ListBox Name="List"></ListBox>
In my C# code, I pull a List from the isolated storage. What I want to do ideally is have all the people in that list appear in the ListBox in a nice formatted way, and then when one is selected, easily retrieve the Person from the List<>.
What I am doing at the moment, and what is surely wrong is:
foreach (Model.Person person in people)
{
List.Items.Add(person.firstName + " " + person.lastName);
}
Then, when an item is selected, I find the person using this method:
string selectedPerson = List.SelectedItem.ToString();
Model.Person person = people.Where(X => X.firstName + " " + X.lastName == selectedPerson).FirstOrDefault();
Obviously, the lists in the item just appear as the plain text and not interesting objects as one might create using ItemTemplate. Could someone please show me what I'm doing wrong or point me in the direction of a good resource for achieving this?
Thanks so much!
You really want to do view related stuff in the view, not in the code behind. Therefore, do not populate the list by hand with code, but instead bind it in the view. The XAML could look something like this:
<ListBox ItemsSource="{Binding Path=People}" SelectedItem="{Binding Path=SelectedPerson, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Use a clear structured ViewModel for your page, with a member People and one for the selected Person. It would look something like:
public class YourPageViewModel : INotifyPropertyChanged
{
public List<Model.Person> People { get; set; }
private Model.Person _selectedPerson;
public Model.Person SelectedPerson
{
get
{
return _selectedPerson;
}
set
{
if (_selectedPerson != value)
{
_selectedPerson = value;
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Then set the viewmodel in the code behind of the view:
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
if (DataContext == null)
DataContext = new YourPageViewModel();
};
}
}
Et voila, you are using a MVVM approach, instead populating the view from the code behind.
Also, I recommend not abusing IsolatedStorageSettings for storing actual data for the app. Use a serializer for your model objects, or take the SQL compact database for Windows Phone to do that.
I would also recommend using ObservableCollection<Person> instead of List<Person>. List doesn't have a built in notification mechanism as does ObservableCollection.