I am trying to implement a numberBox class inherited from TextBox
public class numberBox : TextBox
And I declared a custom DependencyProperty numberProperty
public static readonly DependencyProperty numberProperty = DependencyProperty.Register("number", typeof(object), typeof(numberBox), new PropertyMetadata(0));
public object number
{
get { return GetValue(numberProperty); }
set { SetValue(numberProperty, value); }
}
In constructor of numberBox, I have a binding to synchronize Text and number
public numberBox()
{
Binding b = new Binding("number");
b.Source = this;
this.SetBinding(TextProperty, b);
}
In one case when I use the numberBox like this way
<BC:numberBox x:Name="numC1" number={Binding ElementName=dg, Path=SelectedItem.C1} />
"dg" is a DataGrid, my goal is that when DataGrid selection changed, the numberBox display the value of selected item
P.S I know I can use DataGrid.SelectionChanged event to achieve the same behavior but I just want to learn more about binding
Everything is working fine so far, when I select different row of DataGrid, the numberBox displays correct value, however, when the numberBox got focus, be edited, after losing focus, the numberProperty binding was gone, which means when DataGrid selected item changed, it doesn't bring the value into numberBox anymore
I set a break point and check "this.GetBindingExpression(numberProperty)" in numberBox, and it returns null after this numberBox be edited and lost focus
Does anyone knows the reason and how should I do to fix this?
Many thanks.
You can fix it by setting
b.Mode = BindingMode.OneWay;
in the constructor of numberBox.
By the binding back of TextProperty to source "number" the binding between numberProperty and the grid is cleared.
Related
Hi I want to set the Text property of a Textbox by code behind. At the moment I do using XAML:
<TextBox x:Name="txtFilter" Text="{Binding FiltroFunzioni, Mode=OneWayToSource}" Grid.Row="0" />
As test I did this:
Binding b = new Binding();
b.Mode = BindingMode.OneWayToSource;
b.Path = new PropertyPath("Text"); //??
b.Source = PageViewModel.FiltroFunzioni;
BindingOperations.SetBinding(txtFilter, TextBlock.TextProperty, b);
The variable "FiltroFunzioni" is a string defined as property:
private string _filtroFunzioni = "";
public string FiltroFunzioni
{
get { return _filtroFunzioni; }
set
{
_filtroFunzioni = value;
RaisePropertyChanged("FiltroFunzioni");
_functionsView.Refresh();
}
}
Basically I dunno what kind of value should I set as PropertyPath. Any ideas?
You don't need the PropertyPath here. If you just remove it, your binding should work.
That being said, you should bind in XAML wherever possible.
If your issue is that changes to FiltroFunzioni don't update your textbox, that's because your binding is specifically declared as OneWayToSource: that means that changing the UI changes the source, but changing the source doesn't change the UI. If that isn't what you want, set the Mode to something else, like "TwoWay" - then changes to the source change the UI, AND changes to the UI change the source.
EDIT:
If you really want to bind from your ViewModel instead of just using XAML, TwoWay binding requires utilizing the Path for some reason, when binding through C#. Either of the following solutions work:
b.Source = FiltroFunzioni;
b.Path = new PropertyPath(".");
b.Source = this;
b.Path = new PropertyPath("FiltroFunzioni");
Note that with TwoWay binding you have to either initialize your FiltroFunzioni by setting the TextBox.Text property in your XAML, or setting FiltroFunzioni after the binding was initialized. Otherwise, WPF will immediately override it from the (by default empty) Text in your TextBox.
Anyone please try this scenario and share idea to resolve the issue which am facing.
Scenario:
In my class(inherited from Control) just am have declared the property FlowDirection which is the type of BulletGraphFlowDirection(Enum (Forward, Backward)).
I have used the new Keyword to FlowDirection to resolve warning which I get.
Warning 'DirectionSfBulletGraph1.MyClass.FlowDirection' hides inherited member 'System.Windows.FrameworkElement.FlowDirection'. Use the new keyword if hiding was intended.
public enum BulletGraphFlowDirection
{
Forward,
Backward
}
public class MyClass : Control
{
public new BulletGraphFlowDirection FlowDirection
{
get { return (BulletGraphFlowDirection)GetValue(FlowDirectionProperty); }
set { SetValue(FlowDirectionProperty, value); }
}
// Using a DependencyProperty as the backing store for FlowDirection. This enables animation, styling, binding, etc...
public new static readonly DependencyProperty FlowDirectionProperty =
DependencyProperty.Register("FlowDirection", typeof(BulletGraphFlowDirection), typeof(MyClass), new PropertyMetadata(BulletGraphFlowDirection.Backward));
}
Issue:
When I try to set the property FlowDirection value in Xaml page it’s just thrown the error message” Backward is not a valid value for FlowDirection ”.
<local:MyClass x:Name="myClass" FlowDirection="Backward"/>
My guess is the FlowDirection property is trying to access the value from System.Windows.FrameworkElement. FlowDirection'(Enum(LeftToRight,RightToLeft))
Am not getting error when the property value is set through code behind.
myClass.FlowDirection = BulletGraphFlowDirection.Backward;
Why am I getting issue when declared from Xaml page Its very difficult to found root cause of it. Please share me idea to resolve.
Regards,
Jeyasri M
Well, as you said, it tries to use the value of the original FlowDirection.
The reason is that hiding != overriding. So if your element is stored in a collection of type Control and FlowDirection is called on its elements then the Control.FlowDirection will be called and not the MyClass.FlowDirection. Probably your control is handled as Control and not MyClass when it tries to parse the xaml and initialize the view.
When you set the value in code-behind you specify it explicitly that you want to set the MyClass.FlowDirection by using a MyClass typed variable.
If you would instantiate this variable as:
Control myClass = new MyClass();
Then
myClass.FlowDirection = BulletGraphFlowDirection.Backward;
wouldn't work either in my opinion.
As Karmacon suggested, changing the name will solve the problem. (I guess that forces the xaml parser to handle your control as MyClass and not Control)
Sorry if this has been asked but I can't find it.
I'm trying to bind a textbox to a datasource, but the textbox isn't updating when the datasource changes... Here's my code, can anyone suggest what I'm doing wrong?
Thanks!
public Controller()
{
myForm.databaseTextBinding = new Binding("Text", ac, "connString");
myForm.databaseTextBinding.ControlUpdateMode = ControlUpdateMode.OnPropertyChanged;
myForm.setupSources();
}
public partial class Form1 : Form
{
public Binding databaseTextBinding;
public void setupSources()
{
DatabaseTextBox.DataBindings.Add(databaseTextBinding);
}
}
UPDATE:
I can now make textboxes that will update. I have a new problem though. The datasource itself changes by me making a new ac object. But if I do that, the binding now longer updates. I've considered that maybe the property isn't changing but I'm sure that it is!
Eg this works
ac.cString = "ABC";
ac.cString = "DEF";
but this doesn't...
ac = new AccessConnector(path);
ac.cString = "ABC";
ac.cString = "DEF";
Does the type of the bound object (i.e. ac) implement INotifyPropertyChanged? if so, does the property "connString" raise a PropertyChanged event when it's changed?
if not, you may have a look at the INotifyPropertyChanged sample
EDIT:
the part does not work is due to you did not change your binding to the new object - it's still bound to the old object. when you assign a new object to the reference variable ac, the binding to the original object won't change - thus it still points to the previous object.
Presuming that your Binding is binding a BindingSource to the textbox, you need to add a BindingSource.ResetBindings() to your code.
I would like to create a UserControl containing a DataGrid and then define the columns directly inside my UserControl:
<my:ControlContainingDataGrid ItemsSource="{Binding}">
<my:ControlContainingDataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Property1}" Header="Property 1"/>
In the UserControl I expose the columns of the DataGrid :
static ControlContainingDataGrid()
{
ColumnsProperty = DependencyProperty.Register(
"Columns",
typeof(ObservableCollection<DataGridColumn>),
typeof(ControlContainingDataGrid),
new UIPropertyMetadata(new ObservableCollection<DataGridColumn>())
);
}
[Description("Columns"), Category("Columns")]
public ObservableCollection<DataGridColumn> Columns
{
get { return _datagGrid.Columns; }
}
public static readonly DependencyProperty ColumnsProperty;
=> it doesn't work : the column binded to Property1 is not created.
I try to create the column programatically :
_datagGrid.Columns.Add(new DataGridTextColumn {
Header = "Property 1",
Binding = new Binding {
Path = new PropertyPath("Property1"),
Mode = BindingMode.TwoWay,
},
});
_datagGrid.ItemsSource = testList;
=> it doesn't work : the header is displayed but each row of my DataGrid is empty (bad binding ?).
1- What is the simpliest way to bind the columns of a DataGrid via the UserControl in the XAML part ?
2- What is the simpliest way to bind the columns of a DataGrid via the UserControl programatically ?
I do this programmatically. Pretty much the same code as you have. Works fine. I have never tried to bind the Columns collection itself though I don't see why that should not work.
Your message is a little ambiguous. If you mean to say that your column does not show up, maybe you need to add the columns prior to creating the table. If the rows show up with empty value in the column, then your binding 'Property1' is wrong.
I don't work in XAML so don't know nothing about that.
Scenario: A ListView is DataBound to an ObservableCollection<CustomClass> and is displaying it's items through a custom ItemTemplate. The CustomClass just contains three string properties and one boolean property and already has INotifyPropertyChanged implemented on every of it's four properties. The custom ItemTemplate of the ListView has One-Way bindings on the three string properties and a Two-Way binding on the boolean property, displaying it as a CheckBox.
Problem: I'm looking for the most elegant (in terms of WPF) way to display the count of all checked items in that ListView using a TextBlock - or in other words, all items that have their boolean property set to true in that collection. I want that TextBlock to immediately update the displayed count if one of the ListView items gets checked/unchecked. I know that there are (rather) ugly ways to achieve this with code behind and eventhandling, but I'd like to know if there's a clever way to do this maybe completely in XAML with arcane DataBinding syntax.
Edit: Just as an example/clarification: The ListView displays 100 items, 90 items have their boolean property set to true, so the TextBlock will display '90'. If the user unchecks one more item through it's CheckBox and therefore sets it's property to false through the Two-Way binding, the TextBlock should update to '89'.
Personally I would probably perform this in my ViewModel. Subscribe to the property changed on the items in the ObservableCollection, and then signal the Count property changed on the ViewModel whenever the boolean property changes. In your view simply bind to the Count property.
You could use a Converter to build up a string with the count of the checked items
public sealed class CountToStringConverter : System.Windows.Data.IValueConverter {
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
ObservableCollection<CustomClass> items = value as ObservableCollection<CustomClass>;
int count = 0;
foreach (var item in items) {
if (item.IsChecked) {
count++;
}
}
return count + " Items";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}
Bind the Text-Property of the TextBox to the Collection.
<TextBox Text={Binding Items, Converter={StaticResource countToStringConverter}}/>
UPDATE:
This Binding works only if the Property Items fires the PropertyChanged-Event, if the Collection is changed.
If it were a simple ASP.NET form, I'd look at using JQuery to count the selected items in the ListBox. That may still be a viable option in WPF:
var count = 0;
$('#multiItemListBox :selected').each(count++);
Plug this code into a JS event handler for the OnChange event of the ListBox. You'll have to know what the ListBox would actually be called in the HTML the client gets, and I'm not sure how WPF mashes them up or how to stick the correct reference into server-side XAML.
Thanks for all the answers I've got, these were all applicable solutions but unfortunately not really what I've tried to achieve. So this is how I've solved the problem now:
I've implemented a DependencyProperty on the Window containing the TextBlock:
public static readonly DependencyProperty ActiveItemCountProperty =
DependencyProperty.Register("ActiveItemCount", typeof(int), typeof(CustomControl), new UIPropertyMetadata(0));
On the DataTemplate for the ListView items the CheckBox registered an EventHandler for the Click-Event:
<CheckBox IsChecked="{Binding Active, Mode=TwoWay}" Click="CheckBox_Click" />
The event handler in code behind looks something like this:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<CustomClass> sourceCol = listView.DataContext as ObservableCollection<CustomClass>;
if (sourceCol != null)
ActiveItemCount = sourceCol.Count(x => x.Active);
}
And obviously, the TextBlock is just data bound to this DependencyProperty:
<TextBlock Text="{Binding Path=ActiveItemCount, ElementName=ControlRoot}" />
With ControlRoot being the name of the Window.