I'm new to WPF and binding stuff. I was trying to find a solution to my problem, but still missing something ;/
I would like to get data from TextBox which is in UserControl and put it to a property in which is in a main window, on way it work, but other not ;/
I have a simple class
public class User
{
public string Name {get; set;}
public string Age {get; set;}
}
and a simple user control called UserDetails with a two TextBoxs
<TextBox Text="{Bind Name}" />
<TextBox Text="{Bind Age}" />
UserDetails UC is placed on main window like this:
<uc:UserDetails DataContext="{Binding User, Mode=TwoWay}" />
and in MainWindow ViewModel I have a property
public User User { get; set;}
on MainWindow there is also a button that on click should fill up User property with data from UserDetials UC.
It's quite late now and I need to have it done by morning, so any straight solution would be appreciated.
Any ideas how to do it ? The example above is very simple, but If you could show me the way how to do it, I could handle rest of my problem.
Here are some things to try:
Make sure you're setting your view model to the MainWindow's DataContext. Even if you're not using a separate class for your view model and are just using code-behind, you need to assign the DataContext.
It might just be a typo, but in your text boxes, you've written {Bind Name} when it should be {Binding Path=Name, Mode=TwoWay} and the same for the Age. You need to set the binding mode to two way on the property, not on the class as you have in your example.
You need to implement the INotifyPropertyChanged interface and fire the PropertyChanged event in the property setter. You'll need to do it for your MainViewModel and User classes. Here's an example of how it is done.
Related
I am working on a WPF app using MVVM. On the main windows is a combo box of customer names. When a customer is selected I want to show the addresses for it.
So I created an Address user control and in the control's code behind I added a DP:
public static DependencyProperty CustomerIdProperty =
DependencyProperty.Register("CustomerId", typeof(int), typeof(AddressView));
public int CustomerId
{
get { return (int)GetValue(CustomerIdProperty); }
set { SetValue(CustomerIdProperty, value); }
}
Next, in the main window I bound the combo to the user control's CustomerId DP:
<vw:AddressView Grid.Row="1"
Grid.Column="0"
x:Name="AddressList"
CustomerId="{Binding ElementName=CustomersList, Path=SelectedCustomer.Id, Mode=TwoWay}"/>
I now have a problem and a question:
Problem: When I run this and select a customer, the setter on the DP never fires. The SelectedCustomer property in the Main Window fires, but not the DP in the user control.
Question: How does the ViewModel for the control know about the CustomerId in the DP?
I have created a small sample app here to demonstrate what I'm doing:
http://sdrv.ms/17OZv1x
I would appreciate any help on this.
Thanks
instead of using dependency properties you can go an easy way when your customer object also has the address property
<AdressView>
<TextBlock Text="{Binding Path=MyAddress.Name}" />
<TextBlock Text="{Binding Path=MyAddress.Street}" />
mainwindow
<ComboBox X:Name=cbo .../>
<local:AddressView DataContext="{Binding ElementName=cbo, Path=SelectedItem}"/>
customer.cs
public Address MyAddress {get;set;}
if you want your dependency property stuff to work, you have to post the code for your addressview, so that we can check the bindings to the dependency properties and you have to give some information how you wanna get the address with your customerid.
CustomerList is of type ComboBox and a ComboBox has no property SelectedCustomer. The property you need for your binding is SelectedItem. You should get binding errors during your debug session in Visual Studio. See Output Window.
To get it work you need to update the binding of the CustomerId-Property to the following.
CustomerId="{Binding ElementName=CustomersList, Path=SelectedItem.Id}"
The TwoWay-Binding is only relevant if you want to change the Id from your AddressView. And I think that you donĀ“t want it. So it can be removed.
I basically want to take a bunch of names in a collection and bind them to a combobox. For example:
Bill
Jack
Bob
Kevin
and have those items in a collection and have it bound to the ComboBox. I'm not sure if the list will be updated dynamically or not, but I prefer to plan for it to be. Any help would be appreciated. I've been trying for a few hours now and can't figure it out. I want to do it in XAML and not the code-behind. In the code-behind,
MyComboBox.ItemsSource = MyObservableCollection;
works fine. I don't know how to do that in XAML though with the collection declared in the code-behind.
Thanks in advance (again), community.
*EDIT:
This is how I have the collection declared and accessible.
public ObservableCollection<string> propertynames
{
get {return _propertynames;}
}
private ObservableCollection<string> _propertynames;
The last thing I tried was this:
<Window.Resources>
<CollectionViewSource Source="{Binding propertynames}" x:Key="srcSort"/>
</Window.Resources>
....
<ComboBox x:Name="cboSort" HorizontalAlignment="Left" VerticalAlignment="Top"
Width="256" Background="WhiteSmoke" Margin="12,50,0,0" FontSize="12pt"
Height="27.28"
SelectedIndex="0"
SelectionChanged="cboWorkCenters_SelectionChanged"
ItemsSource="{Binding Path = {StaticResource srcSort}}">
</ComboBox>
....
I'm a total n00b to this stuff. Been in it about a week now, so I may have done something really obvious to a seasoned user.
*EDIT #2
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:WpfApplication1"
Title="Window1" Height="226" Width="242"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ComboBox Margin="43,71,40,77"
Name="comboBox1"
ItemsSource="{Binding ob}" />
</Grid>
</Window>
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public ObservableCollection<string> ob
{
get
{
return _ob;
}
}
private ObservableCollection<string> _ob = new ObservableCollection<string>();
public Window1()
{
InitializeComponent();
FillObj();
//comboBox1.ItemsSource = ob;
}
private void FillObj()
{
for (int i = 1; i < 6; i++)
{
_ob.Add(i.ToString());
}
}
}
}
Made above real simple project just to see if I was doing it all wrong. This worked fine so something else must be causing it to fail.
*EDIT #3
*PROBLEM FIXED
For God's sake, I figured it out. I've been on this for HOURS and it's just silly what's caused it to fail.
The solution is this: I wasn't instantiating _propertynames when I declared it. I was querying the class properties with Linq to get the list of properties and then created _propertynames by passing ...GetProperties.ToList<...>() to the constructor. Apparently, you have to instantiate the variable so it hits during InitializeComponent. Unreal.
Once I did that and then added the items to it after the fact, it worked fine.
I wish WPF had a face so I could punch it. I know it's my ignorance of how it works, but I really could have used some kind of message.
Thanks guys for the help. Both of your suggestions were useful once I took care of the root issue.
private ObservableCollection<string> _propertynames
needs to be
private ObservableCollection<string> _propertynames = new ObservableCollection<string>()
There are countless ways of doing this. Once you've created the collection in code-behind, you can:
Call Resources.Add to add it to the window's resource dictionary, and then bind to the resource, e.g. ItemsSource="{Binding {DynamicResource MyList}}".
Give the ComboBox a name (using the x:Name attribute) and set its ItemsSource explicitly in code, e.g. MyComboBox.ItemsSource = myCollection;.
Create a class, make the collection a property of the class, and set the window's DataContext to an instance of that class and bind to it directly, e.g. ItemsSource = "{Binding MyCollectionProperty}".
Make the collection a property of the window, set the window's DataContext to this, and bind to the property (this is essentially the same technique as #3, only you're not creating a new class).
Without setting the window's DataContext, you can still reference a property on it using binding as long as you've given it a name, e.g. {Binding ElementName=MyWindow, Path=MyCollection}. (This is the same as Ross's suggestion.)
Or, without giving the window a name, you can use RelativeSource binding to find the ancestor Window and bind to a property on it. I don't have any confidence in my ability to write a working binding expression that uses RelativeSource off the top of my head, so I'll leave that as an exercise for the reader.
You can set the DataContext of the ComboBox to the instance of your collection, and then set itsItemsSource to {Binding}. You probably wouldn't do this in practice; I mention it just because it seems to be a common mistake for people to set the DataContext of a control without also setting a binding, and then wonder why content from the bound object isn't showing up.
(While I've said "window" in the above, everything I've said is also true for user controls.)
I'm sure there are at least five other ways to do this that I'm not thinking of. Binding is really, really flexible.
What have you tried so far?
I would approach it as follows, assuming the combo box is within a UserControl with a code-behind class containing the public property MyObservableCollection:
<UserControl x:Name="MyCollectionOwnerControl">
<ComboBox ItemsSource="{Binding ElementName=MyCollectionOwnerControl, Path=MyObservableCollection, Mode=OneWay}" />
</UserControl>
Is there a way to bind a Silverlight control to an object (or database table's row) which contains the values of several control's properties, doing so without by define the binding for each property?
For instance:
Let's say I have the class (or entity based on database table's row) with the following values:
class TextBlockValues
{
public string Text{get; set;}
public string HorizontalAlignment{get; set;}
public string VerticalAlignment{get; set;}
}
I want to bind it to a TextBlock in my silverlight application (again without explicit specify the binding for each property).
Thank you for your time.
There are two parts in a binding: DataContext and the actual Binding objects. Once you set up the data context for an item, all the properties, and children will automatically use that.
For example:
<TextBlock Name="CaptionText" Text="{Binding Text}" HorizontalAlignment="{Binding HorizontalAlignment}" Height="20" TextAlignment="Center" FontStretch="Expanded" FontSize="13" />
And in the .cs file:
CaptionText.DataContext = myObject;
If I understand your question right the answer is no. Even though you can set the control's DataContext you still have to bind which property in the control binds to what in the class.
I'm planning a WPF application which will build dynamic grid with textblocks in the viewmodel and then refresh interface (xaml) with the new grid.
I've done the firts step, but i have problems to refresh the view with the new grid.
Is there any example code of how to bind the grid to the xaml that I can have a look at?? I really can't figure this out!
Thanks
You may be approaching this slightly wrongly, hard to say from the question-
Generally to show a dynamic set of UI elements in MVVM you bind the ItemsSource property of an ItemsControl to an ObservableCollection. The ItemsControl ItemsTemplate property converts the YourViewModel object into a UIElement which can be a TextBlock or whatever style you want.
So as an example:
// model
class Person
{
public string Name {get; private set;}
}
// view model
class MainViewModel
{
public ObservableCollection<Person> People {get; private set;}
}
//view
<UserControl DataContext="{Binding MyMainViewModelObject}">
<ItemsControl ItemsSource="{Binding People}">
<ItemsControl.ItemsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>/
</ItemsControl.ItemsTemplate>
</ItemsControl>
</UserControl>
I havent tested that code, it is just to illustrate. There are other ways of dissecting the problem into MVVM, it all depends on the situation. You would have to give more details for us to help you out with that. Rarely in WPF is there a need to use code to create or add UI elements to other UIElements etc.
A point to note more along the exact lines of the question however is that an ItemsControl can either bind to a bunch of regular objects and use it's template to create UIElements from them, OR it can bind to a list of UIElements, in which case the template is not applied (sounds like this is the situation you have).
I've got a ComboBox with an ItemsSource which I've bound to a List(Of String).
What I'd like to do is have the XAML update a String property when the SelectedValue of the ComboBox changes. I've seen a whole bunch of examples for TextBoxes which use
Text="{Binding Path=MyString}"
sort of stuff, but I don't really think that'll be the way to go if, in future, I need to change the ItemsSource to a List(Of ObscureObject)...
Binding to the selected property of a combobox is fairly simple.
XAML :
<ComboBox ItemsSource={Binding Path=MyCollection} SelectedItem={Binding Path=MyItem}/>
CodeBehind :
public List<string> MyCollection {get; set;}
public string MyItem {get; set;}
If you want to insert text into the selected item, you'll need to use INotifyPropertyChanged
as for your scalability issue, its a fairly minor change to update the type of a property to reflect a collection. Otherwise you could try binding to an Object although that would mean you would constantly have to recast the object back to the state you want.
You can use SelectedItem property of ComboBox to achieve this.
<ComboBox ItemsSource="{Binding Path=YouList}"
SelectedItem="{Binding Path=MyString}" />
When you change your list in future you will have to bind the SelectedItem with a property of your objects type.
Have a look at this article for more details -
http://japikse.blogspot.com/2008/10/wpf-combobox-selecteditem-selectedvalue.html