I have a listbox in my silverlight usercontrol and I am filling it with a generic list of a private class, for some reason it is not being databound.
Here is the code :
class userClient
{
public int characterID { get; set; }
public string characterName { get; set; }
}
List<userClient> userClientList; // = new List<userClient>();
void _client_UserList(object sender, DataTransferEventArgs e)
{
this.Dispatcher.BeginInvoke(() =>
{
userClientList = new List<userClient>();
foreach (string user in e.DataTransfer.Data)
{
var userDetailsArray = user.Split('+');
userClient uc = new userClient
{
characterID = Convert.ToInt32(userDetailsArray[0]),
characterName = userDetailsArray[1]
};
userClientList.Add(uc);
}
chatUsers.ItemsSource = userClientList;
chatUsers.DisplayMemberPath = "characterName";
});
}
I checked the generic list userClientList and it is being filled up so there is no problems there.
This is the XAML of the listbox:
<ListBox x:Name="chatUsers" Grid.Row="0" Grid.Column="1" Margin="2 2 2 2" />
Do you have any binding errors messages logged in Output Window in Visual Studio?
Edit:
Just noticed that your collection is a field while it should be public property
public ObservableCollection<userClient> userClientList { get; set; }
Thank you! Thank you Mokosh! The error --
System.Windows.Data Error: 39 : BindingExpression path error --
drove me crazy through two WPF books without finding the answer that you emphasized --
I didn't create properties on my class members.
Related
This question already has answers here:
DataBinding in WPF?
(1 answer)
Why does WPF support binding to properties of an object, but not fields?
(2 answers)
Closed 1 year ago.
Just when I think I've got a handle on WPF/XAML and view model binding I hit a snag. Here's a streamlined example of an issue I'm having:
Consider the following classes:
namespace TestApp
{
public class BandMember
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Band
{
public BandMember[] member = new BandMember[4];
public Band()
{
for (int i= 0; i < 4; i++)
{ member[i] = new BandMember(); }
}
}
public class Modify
{
public static Band SetBandMembers()
{
Band b = new();
b.member[0].FirstName = "John";
b.member[0].LastName = "Lennon";
b.member[1].FirstName = "Paul";
b.member[1].LastName = "McCartney";
b.member[2].FirstName = "Ringo";
b.member[2].LastName = "Starr";
b.member[3].FirstName = "George";
b.member[3].LastName = "Harrison";
return b;
}
}
}
In the View Model I have
namespace TestApp
{
class ViewModel
{
public Band Beatles { get; set; }
public string Test { get; set; }
public ViewModel()
{
Beatles = new();
Beatles = Modify.SetBandMembers();
Test = "This is the test";
}
}
}
And in the XAML:
<TextBlock Text="{Binding Beatles.member[3].LastName}"
Grid.Row="0"
Width="200" Height="60"/>
<TextBlock Text="{Binding Test}"
Grid.Row="0"
Width="200" Height="60"/>
I know the view model binding is correct, as the {Binding Test} text block displays correctly. But trying to bind to Beatles fails, the text block is blank.
I tried the exact same thing in a console app, with the same classes and calling the same code found in the view model constructor, and was able to write ln the Beatles members' values.
The error I get in VS says when I look at the debugging trace says:
Error: 40 : BindingExpression path error: 'member' property not found on 'object' ''Band' (HashCode=30265903)'.
BindingExpression:Path=Beatles.member[3].LastName;
DataItem='ViewModel'
I have a combobox, which draws it's items from an ObservableCollection of a custom type using Bindings. I've set the DisplayMemberPath so it displays the correct string and stuff. Now I'm fiddling with the SelectedItem/SelectedValue. It needs to be dependant on the selected item of a ListBox, which is bound to a different ObservableCollection of another custom type, but which has a property of the same type of the ComboBox list.
How can I bind this using MVVM? Is it even possible?
I've got my code here:
MainWindowViewModel.cs
private ObservableCollection<Plugin<IPlugin>> erpPlugins;
public ObservableCollection<Plugin<IPlugin>> ERPPlugins
{
get
{
return erpPlugins;
}
set
{
erpPlugins = value;
OnProprtyChanged();
}
}
private ObservableCollection<Plugin<IPlugin>> shopPlugins;
public ObservableCollection<Plugin<IPlugin>> ShopPlugins
{
get
{
return shopPlugins;
}
set
{
shopPlugins = value;
OnProprtyChanged();
}
}
private ObservableCollection<Connection> connections;
public ObservableCollection<Connection> Connections
{
get {
return connections;
}
set
{
connections = value;
}
}
public MainWindowViewModel()
{
instance = this;
ERPPlugins = new ObservableCollection<Plugin<IPlugin>>(GenericPluginLoader<IPlugin>.LoadPlugins("plugins").Where(x => x.PluginInstance.Info.Type == PluginType.ERP));
ShopPlugins = new ObservableCollection<Plugin<IPlugin>>(GenericPluginLoader<IPlugin>.LoadPlugins("plugins").Where(x => x.PluginInstance.Info.Type == PluginType.SHOP));
Connections = new ObservableCollection<Connection>
{
new Connection("test") { ERP = ERPPlugins[0].PluginInstance, Shop = ShopPlugins[0].PluginInstance } // Debug
};
}
Connection.cs
public class Connection
{
public string ConnectionName { get; set; }
public IPlugin ERP { get; set; }
public IPlugin Shop { get; set; }
public Connection(string connName)
{
ConnectionName = connName;
}
}
And the XAML snippet of my ComboBox:
<ComboBox
Margin="10,77,232,0"
VerticalAlignment="Top"
x:Name="cmbERP"
ItemsSource="{Binding ERPPlugins}"
SelectedItem="{Binding ElementName=lbVerbindungen, Path=SelectedItem.ERP}"
DisplayMemberPath="PluginInstance.Info.Name"
>
Alright, I solved it by changing the IPlugin type in the Connection to Plugin. Why I used IPlugin there in the first place is beyond my knowledge. But like this, I have the same type of Plugin everywhere.
Thanks for your help, appreciate it
I was following this link here: http://jacobmsaylor.com/?p=1270
but i'm having problems with it, trying to make tweaks to it
<ListBox Name="PageList_ListBox" MouseDoubleClick="PageList_ListBox_OnMouseDoubleClick"
Background="#FFC9C9C9" Margin="0,5,0,0" ItemsSource="{Binding PageCollection, ElementName=This}">
.
public static ObservableCollection<MLBPage> _PageCollection = new ObservableCollection<MLBPage>();
public static ObservableCollection<MLBPage> PageCollection
{
get { return _PageCollection; }
}
public ICollectionView _PageCollectionView { get; set; }
_PageCollectionView = CollectionViewSource.GetDefaultView(_PageCollection);
private bool FilterLeadersList(object item)
{
MLBPage page = item as MLBPage;
if (page.templateName.Contains("Leaders List"))
{
return true;
}
else
{
return false;
}
}
My MLBPage object has 2 types... where the "templateName" can be either "Leaders List" or "Leader Headshots".. now when I filter the collection by adding to a button:
_PageCollectionView.Filter = FilterLeadersList;
the whole collection just filters (the _PageCollection binded to a listbox turns blank) instead of only the items that contain "Leaders List" within the name....
any help on how i can modify this to work?
change your code into:
private ObservableCollection<MLBPage> _PageCollection = new ObservableCollection<MLBPage>();
public ICollectionView _PageCollectionView { get; set; }
just do this once (eg. within ctor)
//ctor
_PageCollectionView = CollectionViewSource.GetDefaultView(_PageCollection);
_PageCollectionView.Filter = FilterLeadersList,
use clear, add , remove to alter your _PageCollection.
bind your listbox to your view
<ListBox ItemsSource="{Binding _PageCollectionView}"/>
use Refresh to refresh your filter
_PageCollectionView.Refresh();
If I have a observablecollection in a page that inserts items on a listview. How can I add to that same observablecollection(listview) from a different window(class)? I do not want to use INotifyPropertyChanged and all that. All I'm trying to do is add a item to the existing listview. I have tried literally everything and I can't figure it out. Please any help is appreciated.
CampersPage...(BindingCamper is just my way of basically saying new ObservableCollection()
public partial class CampersPage : Page
{
MainWindow _parentForm;
public GridViewColumnHeader currentColumnSorted = null;
private SortAdorner currentAdorner = null;
String request1;
String request2;
String request3;
String request4;
// public ObservableCollection<Camper> Campers { get; private set; }
public CampersPage(MainWindow parent)
{
_parentForm = parent;
InitializeComponent();
_parentForm.bindings = new BindingCamper();
for (int i = 0; i < _parentForm.allCampers.Count; i++)
{
if (_parentForm.allCampers[i].getRequest(1) != null && _parentForm.allCampers[i].getRequest(2) != null && _parentForm.allCampers[i].getRequest(3) != null && _parentForm.allCampers[i].getRequest(4) != null)
{
request1 = _parentForm.allCampers[i].getRequest(1).getName();
request2 = _parentForm.allCampers[i].getRequest(2).getName();
request3 = _parentForm.allCampers[i].getRequest(3).getName();
request4 = _parentForm.allCampers[i].getRequest(4).getName();
}
_parentForm.bindings.Campers.Add(new Camper { FirstName = "" + _parentForm.allCampers[i].getFirstName(), LastName = "" + _parentForm.allCampers[i].getLastName(), Ages = _parentForm.allCampers[i].getAge(), SchoolGrade = _parentForm.allCampers[i].getGrade(), Gender = "" + _parentForm.allCampers[i].getGender(), bindingRequest1 = request1, bindingRequest2 = request2, bindingRequest3 = request3, bindingRequest4 = request4 });
//DataContext = _parentForm.bindings;
}
DataContext = _parentForm.bindings;
}
---Now I click on a button and a new window comes up where I would like to add a new camper to the listview in CampersPage.
public partial class AddNewCamper : Window
{
MainWindow _parentForm;
public AddNewCamper(MainWindow parentForm)
{
InitializeComponent();
_parentForm = parentForm;
// _parentForm.bindings = new BindingCamper();
}private void btnSubmitNewCamper_Click(object sender, RoutedEventArgs e)
{
String firstName = txtNewFirstName.Text;
String lastName = txtLastName.Text;
int age;
int grade;
String newage = comboNewAge.Text;
if (firstName != "" && lastName != "" && IsNumber(txtNewGrade.Text) && newage != "")
{
age = Convert.ToInt16(newage);
grade = Convert.ToInt16(txtNewGrade.Text);
// Create New Camper
Camper person = new Camper(age, grade, boxNewGender.Text, firstName, lastName);
_parentForm.allCampers.Add(person);
//This is just adding the camper to the listview. Not sure if it is actually adding it to the database.
_parentForm.bindings.Campers.Add(new Camper { FirstName = person.getFirstName(), LastName = person.getLastName(), Ages = person.getAge(), SchoolGrade = person.getGrade() });
//CampersPage p = new CampersPage(_parentForm);
DataContext = _parentForm.bindings;
Do I have to somehow add AddNewCamper's namespace to CampersPage's namespace in xaml?
<ListView HorizontalAlignment="Stretch" Margin="0,12" x:Name ="listViewCampers" ItemsSource="{Binding Campers}" DisplayMemberPath="bindMe" IsSynchronizedWithCurrentItem="True" Grid.Column="1">
ObservableCollection class:
public partial class BindingCamper
{ // This class assist in binding campers from listview to the textboxes on the camperspage
public ObservableCollection<Camper> Campers { get; set; }
public ObservableCollection<Staff> StaffMembers { get; set; }
public ObservableCollection<Schedule> schedule { get; set; }
public ObservableCollection<Group> Groups { get; set; }
public BindingCamper()
{
Campers = new ObservableCollection<Camper>();
StaffMembers = new ObservableCollection<Staff>();
schedule = new ObservableCollection<Schedule>();
Groups = new ObservableCollection<Group>();
}
I won't go as far as claiming you're using WPF wrong, but you're certainly making your life difficult. I suggest reading up a bit on MVVM pattern - it really makes WPF development easier (here's good starting article).
Approach you're using at the moment is not correct on several levels:
your windows/pages need to have way too much knowledge about each other to work properly
result of which, is dependency on parent form in your child windows (while what you really need is dependency on window context, which in fact is campers list)
you need to do too much manual notifications/setting up to achieve your goals (while WPF has great tools to do it automatically)
you seem to be exposing model (allCampers) through view (MainWindow)
All of this can be solved with a bit of redesigning:
Your views (Main, CampersPage, AddNewCamper) should be dependent on BindingCamper class (which essentially could be view model for them), not on each other
Same instance of BindingCamper should be set as DataContext for all of them
You should not add bindings manually (like you're doing now); all can (and should) be done from XAML
Having above in mind, your CampersPage class should look like this:
public partial class CampersPage : Page
{
public CampersPage(BindingCamper camper)
{
InitializeComponent();
DataContext = camper;
}
}
It should by no means initialize data for parent window and set it's binding. This is simply wrong.
Actually, this approach (providing data context through constructor) can be used in all your view classes (AddNewCamper and MainWindow too, probably).
Now, when you need campers page, say from your main window, it gets very easy:
public partial class MainWindow : Window
{
public void ShowCampers()
{
var campersPage = new CampersPage((BindingCampers) this.DataContext);
// show campersPage
}
}
It is the same with AddNewCamper window. Just pass it data context. When you add new camper, add it to BindingCamper.Campers list (which is available through data context):
// rest of the btnSubmitNewCamper_Click method elided
Camper person = new Camper(age, grade, boxNewGender.Text, firstName, lastName);
((BindingCamper)this.DataContext).Campers.Add(person);
That's all. Thanks to combined mechanisms of data binding and observable collection, this new element will immediately be visible both in MainWindow and CampersPage.
Edit:
Code to fetch campers from database should be wrapper with some kind of DAO object (as a part of DAL - I know, lot of ugly buzzwords, but luckily they are all related and fairly obvious). For example, you can have a class that will deal with getting campers from database:
public class CampersProvider
{
public IEnumerable<Camper> GetAllCampers()
{
// here you put all your code for DB interaction
// and simply return campers
}
}
To give you quick solution, you can once again pass CampersProvider to MainWindow constructor, call GetAllCampters method and build observable collection for BindingCamper. However, this is not very MVVM approach. Those stuff usually is handled by view model (yet another, separate class), which at the moment you don't have.
Code you posted requires quite some work, I think it won't be a bad idea if you read a bit about MVVM pattern and try to apply it to your application.
Im struggling with Observable collections and using it to add pushpins onto a silverlight bing map. Im trying to build up a collection here using Linq. But im getting the error under every "PushPinItems" instance in my code saying:
'observable_collection_test.Map.PushPinItems' is a 'field' but is used like a 'type' c:\users\dan\documents\visual studio 2010\Projects\observable collection test\observable collection test\Map.xaml.cs 26 38 observable collection test
Not sure whats going on here, am I declaring/constructing it wrong or something?
Im new to Observable collections (and most of c#!) so any help/advice welcome. Many thanks.
UPDATE:
This seems to be ok now, the above issue, but now its not binding my items to pushpins.
I have looked at the "PushPins = pushPinCollection;" method and all 143 items are in there with lat, long and location propertiess with the correct data- as per this breakpoint:
Maybe there is an issue with my XAML binding?
Here is the updated code:
namespace observable_collection_test
{
public partial class Map : PhoneApplicationPage
{
private ObservableCollection<SItem2> _PushPins;
public event PropertyChangedEventHandler PropertyChanged;
public Map()
{
InitializeComponent();
getItems();
}
public ObservableCollection<SItem2> PushPins
{
get
{
return _PushPins;
}
private set
{
_PushPins = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("PushPins"));
}
}
}
private GeoCoordinate _location;
public GeoCoordinate Location
{
get { return _location; }
set
{
if (_location != value)
{
_location = value;
}
}
}
private string _pinSource;
public string PinSource
{
get { return _pinSource; }
set
{
if (_pinSource != value)
{
_pinSource = value;
}
}
}
public void getItems()
{
var document = XDocument.Load("ListSmall.xml");
if (document.Root == null)
return;
var xmlns = XNamespace.Get("http://www.blahblah.co.uk/blah");
var events = from ev in document.Descendants("item")
select new
{
Latitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "lat").Value),
Longitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "long").Value),
};
ObservableCollection<SItem2> pushPinCollection = new ObservableCollection<SItem2>();
foreach (var ev in events)
{
SItem2 PushPin = new SItem2
( ev.Latitude, ev.Longitude)
{
};
pushPinCollection.Add(PushPin);
}
PushPins = pushPinCollection;
}
other class:
namespace observable_collection_test
{
public class SItem2
{
//public DateTimeOffset Date { get; set; }
//public string Title
//{ get; set; }
public double Latitude
{ get; set; }
public double Longitude
{ get; set; }
public GeoCoordinate Location
{ get; set; }
//public Uri Link { get; set; }
public SItem2(//string Title,
double Latitude, double Longitude)
{
//this.Date = Date;
//this.Title = Title;
this.Latitude = Latitude;
this.Longitude = Longitude;
//this.Location = Location;
//this.Link = Link;
}
}
Bit of XAML concerning adding pins to map:
<my:Map ZoomBarVisibility="Visible" ZoomLevel="10" CredentialsProvider="AhqTWqHxryix_GnWER5WYH44tFuutXNEPvFm5H_CvsZHQ_U7-drCdRDvcWSNz6aT" Height="508" HorizontalAlignment="Left" Margin="0,22,0,0" Name="map1" VerticalAlignment="Top" Width="456">
<my:MapItemsControl ItemsSource="{Binding PushPins}" >
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Background="Aqua" Location="{Binding Location}" ManipulationCompleted="pin_click">
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
</my:Map>
It would also be good to know if I am approaching the pushpin binding to the maps in the right way.
It looks as if this is because you have used x:Name="PushPinItems" in your XAML which is the same name as one of your types, so when you think you are referencing your PushPinItems type in your codebehind, you are actually referencing the field that VS has generated for you from your XAML that represents that Pushpin instance. You could use a different x:Name in your XAML.
Update
Ok, I see the issue :) I haven't worked with the Bing maps control before, but looking at http://forums.silverlight.net/forums/t/197631.aspx (second post down), you need to set the map controls MapItemsControl property. The ItemsSource property here should be bound to your ObservableCollection of a custom type which contains properties such as Name and Location. You can then populate this collection with instances of this custom type (in the post they have used MapData as the type name).
You can also get more examples and source code at http://www.microsoft.com/maps/isdk/silverlight/