Why is my Binding not working? - wpf

This is silverlight code - but I guess that this will be same in WPF -
I have this simple classes
public Class A1
{
string str1;
public string Str_1
{
return str1;
}
}
public Class B1
{
A1 a1;
public A1 A_1
{
return a1;
}
}
I assume that B1::A1.str1 have the value "my string".
Now in the XAML I have this
<Grid x:Name="LayoutRoot" DataContext="B1">
<StackPanel>
<TextBox Text="{Binding ElementName=A1, Path=Str_1, Mode=TwoWay}"/>
</StackPanel>
</Grid>
In the the code ( xaml.cs ) i writing in the constructor this
LayoutRoot.DataContext = this;
( the B1 object is part of the xaml.cs file and the B1 is also not null and A1 is not null )
But ==> this is not working ... and the text of the textbox is not update with the text that is in A1 object.

You are using element binding but A1 is not a named element in the Xaml page.
You want Text={Binding Path=A_1.Str_1}
This means that it points to the Str_1 property of the A_1 property of the data context (your code behind class).
Please note that TwoWay is pointless here as you have no setters on your properties.
To do this properly (assuming your values will change and be required) you need to implement setters on your A_1 and Str_1 properties and implement INotifyPropertyChanged on both your class A1 & B1.

You can say Text={Binding Path=A_1.Str_1, Mode=TwoWay} I think that should work.
Also if you want to do two-way binding you need to implement INotifyPropertyChanged to let WPF know it has to refresh the UI after you updated the value in code.
GJ

Firstly your class B1 must implement INotifyPropertyChanged like this.
Then you should make a property proxy in your B1 class like this:
public string Str_1
{
get
{
return a1.str1;
}
set
{
a1.str1 = value;
this.RaisePropertyChanged("Str_1"); // INotifyPropertyChanged implementation method
}
}
And finally update your binding:
<TextBox Text="{Binding Path=Str_1}"/>

Related

How to get IndexOf Object in .Net ItemCollection

From my ViewModel, I need to programmatically move the focus and highlight of a row in a WPF DataGrid. The DataGrid has just one column:
<DataGrid Name="DgAdrType"
ItemsSource="{Binding ItemsLcv}"
IsSynchronizedWithCurrentItem="True"
<DataGridTextColumn Header=" Description"
IsReadOnly="True"
CanUserSort="True" Binding="{Binding descr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
And in the datacontext ViewModel:
private IEnumerable<AdrTypeMdl> _itemsList;
ItemsLcv = CollectionViewSource.GetDefaultView(_itemsList) as ListCollectionView;
This works even though I don't have a property per se in the ViewModel for the data field "descr", because I bind the DataGrid's ItemSource.
In the ViewModel I can access the View DataGrid's ItemCollection of items by passing in that ItemCollection from the View like so:
<!-- Interaction for click selection -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotMouseCapture">
<i:InvokeCommandAction Command="{Binding SelObjChangedCommand}"
CommandParameter="{Binding ElementName=DgAdrType, Path=Items}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
And back in the ViewModel, I load the DataGrid items like so:
private ItemCollection _dgItems;
private void SelObjChanged(object theItems)
{if (theItems !=null)
{ _dgItems = theItems as ItemCollection;
I want to keep the cast to ItemCollection so that I can retain the DataGrid properties of that ItemCollection. The problem is the ItemCollection's IndexOf method is not working. I only get -1 when I try to find the index of one of the class object items by doing this.
var idx = _dgItems.IndexOf(myobject);
EDIT ------- this is entire code of the method try IndesOf
private void HandleUpdateListEvent(Object myobject)
{AdrTypeMdl theNewItem = myobject as AdrTypeMdl;
bool co = _dgItems.Contains(theNewItem);
var idx = _dgItems.IndexOf(theNewItem);
_dgItems.MoveCurrentToPosition(idx);
_dgItems.Refresh();}
EDIT ---------------------------------
This is the easier approach but I still need help with the lambda / filter expression and method call
// this is where I try to get the index of an object for highlighting
private void HandleUpdateListEvent(Object myobject)
AdrTypeMdl theNewItem = myobject as AdrTypeMdl;
var e = ItemsLcv.SourceCollection.GetEnumerator();
ItemsLcv.Filter = o => (o == theNewItem);
foreach (row in ItemsLcv)
{ if row == theNewItem
return e >;
e = -1;}
ItemsLcv.MoveCurrentToPosition(e);
ItemsLcv.Refresh();}
END EDIT ---------------------
In debugger I can see the class Objects in _dgItems. If I do this, it works.
var idx = _dgItems.IndexOf(_dgItems[2]);
But the IndexOf method does not work when the parameter is just a class Object. I think the problem is with my cast of the DataGrid items to an ItemCollection. I need to cast the class Object, ie. myobject, to something recognizable by the ItemCollection that I got from the DataGrid. Is there a workaround? Thank you.
Try this.
You need to cast it to the type of collection ie AdrTypeMdl. You cannot simply get the index by passing an object. You're binding to a source ItemsLcv which is of type AdrTypeMd1. So pass that exact type to get the exact index.
var dgcolumn = myobject as AdrTypeMdl;
if(dgcolumn != null)
{
var idx = _dgItems.IndexOf(dgcolumn);
}
idx will be the index of that corresponding column.

Bind a property based on a dictionary key?

This seems to be a tricky one.
You have a data object which you bind to a number of user controls.
Simplified:
public class Shift : INotifyPropertyChanged
{
private int _baseColor;
public int BaseColor
{
get { return _baseColor; }
set
{
_baseColor = value;
OnPropertyChanged("BaseColor");
}
}
private Employees_E _employee;
public Employees_E Employee
{
get
{
return _employee;
}
set
{
_employee = value;
OnPropertyChanged("Employee");
}
}
And here is the deal:
Depending on the Employee, the user control will change its background. One way to solve that (which works fine) is of course to use a converter, like...
Background="{Binding Employee, ElementName=uc, Converter={StaticResource EmployeeValueConverter}
But when all of it is dynamic it complicates things a lot. I don't know in advance the number of, or the name of the Employee, or the associated color to the employee.
What I want is my user control to bind to a Dictionary so that Shift.BaseColor binds to the value where the key is Employee. Something like:
Background="{Binding BaseColor, ElementName=anynamespace, Converter={StaticResource AnotherConverter}
Or preferable:
Background="{Binding MyDictionary[Employee].Value... with a converter...
The user will be able to change the associated color without changing the data object so I need another way to update my user controls when the color change.
The value will be an integer and in the converter I return a LinearGradientBrush form a list, so the integer will be an index in that list.
UPDATE 1
The background i will change is a Border background where Multibinding isn't possible? I have found another thread showing how to do it with multibinding but doesn't work in this case...?
UPDATE 2
The problem isn't how to get access to the values or the converter. The problem is when the user change the value in the dictionary and how to properly bind that to make the user control update it's background. Let's say we have a key=MARIA and a value of 21 which is the index of my List. Then the user control has a certain color. But the user might want to associate another color to that key, but there I can't figure out how to bind it.
<UserControl x:Class="SPAS.Controls.ShiftControl" ...
<Border BorderThickness="1" x:Name="myBorder" Background="{Binding BaseColor, ElementName=uc, Converter={StaticResource BaseColorLinear}, Mode=TwoWay}" >
...
</Border>
</UserControl>
public partial class ShiftControl : UserControl
{
public static DependencyProperty BaseColorProperty = DependencyProperty.Register("BaseColor", typeof(int), typeof(ShiftControl), new UIPropertyMetadata(0));
public int BaseColor
{
get { return (int)GetValue(BaseColorProperty); }
set { SetValue(BaseColorProperty, value); }
}
public static DependencyProperty EmployeeProperty = DependencyProperty.Register("Employee", typeof(Employees_E), typeof(ShiftControl), new PropertyMetadata(Employees_E.VAKANT));
public Employees_E Employee
{
get { return (Employees_E)GetValue(EmployeeProperty); }
set { SetValue(EmployeeProperty, value); }
}...
I want the BaseColor to come from
Dictionary<Employees_E, int>
so the user can change either the Employee property or the value in the dictionary.

WPF: What is more easy is convinient to develop dynamically?

I have a DataGrid (dataGrid1) where records can be added and deleted.
Based on that dataGrid1, I want to make a new Grid with buttons in it based on ID and Types'. Cols will also have to given a DataSource of add dynamically, but that will be just while generating for the 1st time in Window_Loaded itself. Rows can be added/removed based on changes in dataGrid1. I want somethign like this :
On each Btn click, a new window will be opened for entry of the particular Type and for the particular ID. If the details are already entered, then the text of btn wil be "Update" else "Add".
What could be the best resource/control to perform this operations ? At present, I just did a Grid with 2 stable cols. Any ideas for the above to use Grid, DataGrid or something else. And adding/removing rows will be easy in which way and how.
Any help is appreciated.
Okay, let me try to take an example which is similar to your needs
Let's assume we use this class:
public class MyObject
{
public int MyID;
public string MyString;
public ICommand MyCommand;
}
And we are willing to display a DataGrid listing the ID, and having as a second column a Button, with the property MyString as content, which, when clicked, launches the ICommand MyCommand which opens in a new window whatever you want.
Here is what you should have on the View side:
<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding MyID}" />
<DataGridTemplateColumn Header="Buttons">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="{Binding MyString}" Command="{Binding MyCommand}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
This will show a DataGrid taking all the content in an IEnumerable<MyObject> named 'MyList', and shows two columns as defined before.
Now if you need to define the command.
First, I recommend you read this introductory link to MVVM and take the RelayCommand class (that's what we're gonna use for your problem)
So, in your ViewModel, the one which defines the MyList, here is how you should define some of the useful objects:
public ObservableCollection<MyObject> MyList { get; set; }
// blah blah blah
public void InitializeMyList()
{
MyList = new ObservableCollection<MyObject>();
for (int i = 0; i < 5; i++)
{
MyList.Add(InitializeMyObject(i));
}
}
public MyObject InitializeMyObject(int i)
{
MyObject theObject = new MyObject();
theObject.MyID = i;
theObject.MyString = "The object " + i;
theObject.MyCommand = new RelayCommand(param =< this.ShowWindow(i));
return theObject
}
private void ShowWindow(int i)
{
// Just as an exammple, here I just show a MessageBox
MessageBox.Show("You clicked on object " + i + "!!!");
}
This should be enough to create whatever you want. As you can see, every Button will call a method (ShowWindow) which is defined to show your new window, do whatever you need inside. The RelayCommand is actually just here, as its name says, to relay the command fired by the button to a method which contains the execution logic.
And... I think that's all you need. Sorry for the late answer BTW
EDIT - generating columns manually/dynamically
The following code is part of a code I had to do when I had a similar problem.
My problem was, I needed to change the columns displayed every time a ComboBox's SelectedItem would change. So I put this in a SelectionChanged event handler.
I don't know where exactly do you need to generate your columns, but I'll give you a general example.
Assume your ItemsSource is an ObservableCollection<MyNewObject>
MyNewObject is the following:
public class MyNewObject
{
public IList<string> MyStrings { get; set; }
}
You should put somewhere in your code (should be when you need to generate the column) the following code, which is generating a number of columns equal to the length of the first MyNewObject from the list (note: this is in code-behind, and the DataGrid you're working on is named dataGrid)
ObservableCollection<MyNewObject> source = dataGrid.ItemsSource as ObservableCollection<MyNewObject>;
if (source == null || source.Count == 0)
{
return;
}
MyNewObject firstObject = source[0];
for(int i = 0; i < firstObject.MyStrings.Count; i++)
{
// Creates one column filled with buttons for each string
DataGridTemplateColumn columnToAdd = new DataGridTemplateColumn();
columnToAdd.Width = 110; // I set a manual width, but you can do whatever you want
columnToAdd.Header = "Header number " + i;
// Create the template with a Button inside, bound to the appropriate string
DataTemplate dataTemplate = new DataTemplate(typeof(Button));
FrameworkElementFactory buttonElement = new FrameworkElementFactory(typeof(Button));
Binding binding = new Binding("MyStrings[" + i + "]");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
buttonElement.SetBinding(Button.ContentProperty, binding);
// Do the same here for your command, or for whatever you want to do when the user clicks on this button
dataTemplate.VisualTree = buttonElement;
columnToAdd.CellTemplate = dataTemplate;
dataGrid.Columns.Add(columnToAdd);
}
This will create one column for each string found in the first object. Then, enhance it with whatever command or display trick you need!

A collection of StackPanel as ItemsSource of ComboBox

I have
a collection of StackPanel which each one includes a dynamic set of controls (based on database values), I want to set them as ItemsSource of some ComboBox
for example i have two database values which should be generated:
In DB i have these:
row 1=>Class [a] p [B] , [AB]vb
row 2=>Class tpy rbs=[sdfg],kssc[h] hm
and each one should generate as a ComboBox column like the fallowing:
In ComboBox I wanna generate these :
ComboBoxItem 1 :Class [a textBox] p [a textBox] , [a textBox]vb
ComboBoxItem 2 :Class tpy rbs=[a textBox].kssc[a textBox] hm
the fallowing code is doing this right:
Class ConvertToControlsFormat()
{
Regex exp = new Regex(#"\[\w*\]");
var source = new TestEntities().cmbSources;
foreach (var item in source)
{
StackPanel p = new StackPanel { Orientation = Orientation.Horizontal, FlowDirection = FlowDirection.LeftToRight };
int i = 0;
foreach (string txt in exp.Split(item.Title))
{
p.Children.Add(new TextBlock { Text = txt });
if (i < exp.Matches(item.Title).Count)
p.Children.Add(new TextBox { Text = exp.Matches(item.Title)[i].Value, Width = 30 });
}
cmb.Items.Add(p);
}
}
But I cant set TwoWay DataBindings for that, so I created a list of StackPanel as a field of cmbSource class (which is bound to ItemsSource of the ComboBox)
public partial class cmbSource
{
#region Primitive Properties
int iD;
public virtual int ID
{
get
{
if (Title != null)
ControlsCollection = SetControlsCollection(Title);
return iD;
}
set
{
iD = value;
}
}
private StackPanel SetControlsCollection(string ttl)
{
Regex exp = new Regex(#"\[\w*\]");
StackPanel p = new StackPanel { Orientation = Orientation.Horizontal, FlowDirection = System.Windows.FlowDirection.LeftToRight };
int i = 0;
foreach (string txt in exp.Split(ttl))
{
p.Children.Add(new TextBlock { Text = txt });
if (i < exp.Matches(ttl).Count)
p.Children.Add(new TextBox { Text = exp.Matches(ttl)[i].Value, Width = 30 });
}
return p;
}
public virtual string Title
{
get;
set;
}
public virtual StackPanel ControlsCollection
{
get;
set;
}
#endregion
}
but I have no idea of how bind it to ItemsSource of my ComboBox
Summery:I want to bind a list of controls to a ComboBox
any suggestions!? thank you.
EDIT
First: you do not bind a ComboBox to a collection of UI Elements. That is not the way WPF works. Container controls such as the Grid, StackPanel and Canvas can contain child controls. ItemsControls such as the ComboBox contain data objects and use DataTemplates to display the items.
Secondly: if the database can contain ANY data that could cause ANY UI to be needed you will need to generate the UI in code by creating StackPanels etc. adding controls and bindings as you do in your code examples.
Thirdly: the reason you can't bind is that the data from the database is a string that you split into parts; there is no way you can simply go back to the string.
Suggestion: the string in the database is probably (I hope) in some sort of format. Using that knowledge you could generate a new format string when you are parsing the database string. E.g., when the database contains foo [bar] you could generate {0} [bar]. On a save action from the user you could use that string to create the updated string for the database by using: String.Format("{0} [bar]", someControl.Text)
Extra: Please, next time, use better names and example texts; the question is unreadable like this. There is no way you can expect us to understand 2=>Class tpy rbs=[sdfg],kssc[h] hm
OLD ANSWER
Make a class Stuff, implementing INotifyPropertyChanged and having the properties Name and Value.
Load the database data into an ObservableCollection<Stuff> and bind the ComboBox to this collection.
Set the ItemTemplate of the combo box to a datatemplate like this:
<ComboBox ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

wpf toolkit, datagrid, comboboxcolumn

In a datagrid I have two DataGridComboBoxColumns. The items of one of these columns should depend on what is selected in the other column. The underlying collection used to model this is a dictionary<string,List<string>>. How should i go about implementing this? I can't seem to hook up to any relevant events on the columns, and I cant find any databinding scenarios that support this..
I had the same scenario a while back and fixed it like this:
public class DataItem : INotifyPropertyChanged {
...
public List<SomeObject> DisplayableComboBoxItems {
get; set;
}
private static Dictionary<int, List<SomeObject>> myDict;
public Dictionary<int, List<SomeObject>> MyDict {
get {
if (myDict == null) {
myDict = GetYourDataFromSomewhere();
}
return myDict;
}
}
public int TypeId {
get { return typeId; }
set {
if (value == typeId) return;
typeId = value;
RaisePropertyChanged("TypeId");
}
}
public int TypeSetId {
get { return typeSetId; }
set {
if (typeSetId == value) return;
typeSetId = value;
RaisePropertyChanged("TypeSetId");
DisplayableComboBoxItems = MyDict[typeSetId];
RaisePropertyChanged("DisplayableComboBoxItems");
TypeId = 0;
}
}
...
}
DataItem is the object that gets bound to a DataRow.
This is just a small mock-up of the code. Basically, whenever the TypeSet changes, I needed a new list of Types to be displayed. I used just a static list, in this example i used a dictionary.
With this setup you can bind you combobox ItemsSource to the 'DisplayableComboBoxItems', and your SelectedValue to "TypeId".
You're gonna need other properties to display the correct text instead of the TypeId.
The downside of this is that when you have 1000+ items, you'll have that same list for all items. This wasn't however the case with me (DataGrid showed max 50 items).
I hope this is clear enough and that it helps you in the right direction!
cheers!
Roel
Instead of using a DataGridComboBoxColumn for the second column, I went with a DataGridTemplateColumn with an embedded Combobox. For the itemsource i defined a converter: string -> List<string>. The converter translates the value of the selecteditem of the other DataGridComboBox (which is bound to Navn) into List<string>, this is just a dictionary lookup.
Like so:
<my:DataGridTemplateColumn>
<my:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Værdi}"
ItemsSource="{Binding Navn, Converter={StaticResource dimensionToValues}}"
>
</ComboBox>
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>

Resources