Problem with [DefaultValue()] inheriting ComboBox - c#

for a project of mine I inherited a ComboBox to change its size behaviour. In addition to this i wanted, to speed up my forms' creation, to set the default DropDownStyle to ComboBoxStyle.DropDownList
To do this i used the [Default()] command overwriting the DropDownStyle property
[DefaultValue(ComboBoxStyle.DropDownList)]
public new ComboBoxStyle DropDownStyle
{
get
{
return base.DropDownStyle;
}
set
{
base.DropDownStyle = value;
}
}
Then i modified the default value in the Designer setting the DropDownStyle to ComboBoxStyle.DropDownList.
And here comes the problem...
There is a small number of InheritedComboBox which i want to have ComboBoxStyle.DropDown because they need to work with
AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Append;
AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.ListItems;
If i set it from the Designer it works fine, however, sometimes, after i rebuild the form, it throws an exception (also at design time) regarding the ComboBoxStyle. When i look to the FormName.Designer.cs file, i can find that for the specific InheritedComboBox there is no
DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown
and I have to add it manually.
This is a little boring because sometimes i just notice it at runtime, when the program throws an exception without showing the form and i cannot test every form every time i rebuild...
Do you have any idea why i get this strange behaviour?
Is there any way to fix it?
Thanks a lot for any answer!

When you set the AutoCompleteMode or AutoCompleteSource property, I believe the designer is looking to the base ComboBox and not generating the line to set the DropDownStyle, since DropDown is the default value for ComboBox.
I was able to correct this by adding an AutoCompleteMode and AutoCompleteSource property to the inherited ComboBox, but also had to add in a line to set the base DropDownStyle because of the order in which the designer sets the properties.
Try something like this and see if it works for you:
public class MyComboBox : ComboBox
{
public MyComboBox()
{
DropDownStyle = ComboBoxStyle.DropDownList;
AutoCompleteMode = AutoCompleteMode.None;
AutoCompleteSource = AutoCompleteSource.None;
}
[DefaultValue(ComboBoxStyle.DropDownList)]
public new ComboBoxStyle DropDownStyle
{
get { return base.DropDownStyle; }
set { base.DropDownStyle = value; }
}
[DefaultValue(AutoCompleteMode.None)]
public new AutoCompleteMode AutoCompleteMode
{
get { return base.AutoCompleteMode; }
set
{
if (value != AutoCompleteMode.None)
base.DropDownStyle = ComboBoxStyle.DropDown;
base.AutoCompleteMode = value;
}
}
[DefaultValue(AutoCompleteSource.None)]
public new AutoCompleteSource AutoCompleteSource
{
get { return base.AutoCompleteSource; }
set
{
if (value != AutoCompleteSource.None)
base.DropDownStyle = ComboBoxStyle.DropDown;
base.AutoCompleteSource = value;
}
}
}

Try setting this value to the property in the constructor of the inherited combobox too to the value you set with DefaultValue. This should probably fix your issue.

Related

Changing binding mode in code?

I'm making a small application, it's a form that reads from a data source, I want to use it for editing and adding new records.
so the default binding mode for the textboxes in the form is TwoWay mode, so the user can edit an existing record, but I want to add a Checkbox that when checked, it marks the data in the textboxes as new, and then adding them to the data source, so I need to change the binding mode to OneWay,
to my knowledge, to do this in code I need to create a new Binding object, that I will have to set properties like Source that doesn't change:
Binding myBinding = new Binding();
myBinding.Source = ViewModel;
myBinding.Path = new PropertyPath("SomeString");
myBinding.Mode = BindingMode.TwoWay;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
So is there a way to change only the Binding mode in code?
EDIT
some further explanation of the application:
In the form there's a combobox that is bound to a List<Book>, there are 3 TextBoxs, their Text properties bound to the DataContext object of their container which itself set to the SelectedItem of the Combobox.
When I added the ReadOnly property as described in the answer, when I check the checkbox I can't change the text in the textboxes.
..
Thanks!
Don't change Mode of binding. Just correct your view-model logic.
public class ViewModel : INotifyPropertyChanged
{
private string _text;
private bool _readOnly;
public string Text
{
get { return _text; }
set
{
if (ReadOnly || value == _text)
return;
_text = value;
OnPropertyChanged(nameof(Text));
}
}
public string ReadOnly
{
get { return _readOnly; }
set
{
if (value == _readOnly)
return;
_readOnly = value;
OnPropertyChanged(nameof(ReadOnly));
}
}
}
In XAML bind IsChecked property of your CheckBox to ReadOnly property.
The answer to this is "Yes, but". The Mode property of Binding has a setter. So it appears that you can set the mode of an existing binding like so...
BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
Binding b = be?.ParentBinding as Binding;
if (b != null)
{
b.Mode = BindingMode.OneWay;
}
However if you do this you will get in every case the exception...
System.InvalidOperationException occurred
HResult=0x80131509
Message=Binding cannot be changed after it has been used.
So the only way to accomplish what you want is to create a new binding based on the old binding while changing the mode. Then replace the old binding.
BindingExpression be = textBox.GetBindingExpression(TextBox.TextProperty);
Binding b = be?.ParentBinding as Binding;
if (b != null)
{
Binding b2 = new Binding();
b2.Path = b.Path;
b2.Mode = BindingMode.OneWay;
textBox.SetBinding(TextBox.TextProperty, b2);
}
This is less than ideal because for completeness you would need to copy the converter, the converter parameter and so on, but this is the best you can do.

Issue with property not passing back value to text box on property change

I am working on a WPF application and i have a textbox bound (bidirectionally) to a property in my view model.
I am trying to prevent a user from typing more than 100 characters into this textbox (this is the max the database will store) so i have written this.
public abstract class AppBaseViewModel : ViewModelBase
{
private String _text;
public String Text
{
get { return _text; }
set
{
_text = CheckTextLength(value, _text);
OnPropertyChanged("Text");
}
}
private string CheckTextLength(string value, string text)
{
if (value.Length < 100)
{
return value;
}
else
{
return text;
}
}
}
All this code seems to do is save the first 100 characters to the field but it still allows the user to carry on typing past 100 characters... i would guess it is because the field value isn't being passed back to the textbox.
I don't understand why this doesn't work as i did something similar using MVVM Light's RaisePropertyChange() in a different application.
It is worth noting that i am unable to access the designer for the textbox so cannot set the .Net textbox property for max length.
Edit: Just for clarification i cannot view or edit the xaml as some are suggesting as i do not have access to the XAML file (i know, it's stupid). All the bindings we use are two way by default
Have you tried with TextBox.MaxLength ?
<TextBox MaxLength="100"/>
Gets or sets the maximum number of characters that can be manually entered into the text box.
If no access to the XAML, eventually get access to the XAML instead of parsing and verifying lengths of arrays and use substrings here and there. At least that's what i would do for this simple issue or talk to the designer to add that small piece of code.
Update 1
public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
Go and get that child and set its MaxLength. This is just a slight modification on the View so it will not affect the MVVM pattern.
OK. I'm not at all sure that I'm proud of this, but am presenting it as an alternative.
You can change the UpdateSourceTrigger of the TextBox's Text property by applying a universal Style to all of the TextBoxes. This is only going to be practical in pretty weird arrangements, but the question is a little unusual in itself.
XAML codebehind:
//I'm using MVVM Light here - you need to be able to find an instance
//of your AppBaseViewModel somehow.
private ViewModelLocator _locator;
//View codebehind constructor, may need to change names as appropriate
public AppBaseView()
{
InitializeComponent();
//MVVM Light again
_locator = new ViewModelLocator();
//Create the binding
Binding binding = new Binding();
//Source = The instance of your ViewModel
binding.Source = _locator.AppBaseViewModel ;
binding.Path = new PropertyPath("Text");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
//Create a Style with no Key - this will apply to *all* TextBoxes
//without their own explicit Style set.
Style style = new Style(typeof(TextBox));
style.Setters.Add(new Setter(TextBox.TextProperty, binding));
//Add the Style to the XAML's Resources:
Resources.Add(typeof(TextBox), style);
}
The view won't listen to the PropertyChanged notification if it's currently trying to change the property itself.
The only thing that comes to mind is launching an extra delayed PropertyChanged notification when you detect the constraint is not met...
private string CheckTextLength(string value, string text)
{
if (value.Length < 100)
{
return value;
}
else
{
MyDispatcher.BeginInvoke(new Action(() =>
OnPropertyChanged("Text")),
DispatcherPriority.Loaded);
return text;
}
}
Can't try the code, so sorry if it doesn't build righ away. MyDispatcher could be your Application.Current.Dispatcher, for instance.
The xaml view /the binding is only updated when the textbox has lost focus. if the text entered is <100 then the value is set otherwise _text is set. this means that initially _text has no value so null will be set upon the if statement being false. i also suggest yo use RaisePropertyChanged(); and when used within the property itself no parameter is needed.

Viewstate set Property value

I use viewstate in application where I have found that encapsulating viewstate in property is much more convenient.
Here is the code what I do in my code.
public List<TransactionEntity> TransactionData
{
get
{
if(ViewState["TransactionData"] == null)
return new List<TransactionEntity>();
return _TransactionData as List<TransactionEntity>();
}
set
{
How to set value to gridview from here ??
}
}
Now i have a gridview in user control which I need to bind everytime I assign value to this property.
Anyone knows how to do it.
Thanks.
Now i have a gridview in user control which I need to bind everytime I
assign value to this property.
Anyone knows how to do it
set
{
ViewState["TransactionData"] = value;
gridview.DataSource = value as List<TransactionEntity>; //check if null etc
gridview.DataBind();// ...
}

How to block assign value in setter in WPF

I have for example property like this:
private string foobar;
public string Foobar
{
get
{
return this.foobar;
}
set
{
if (value != this.foobar)
{
// here I want to check if value is correct
if(value != something)
{
this.foobar = value;
this.NotifyPropertyChanged("Foobar");
}
else
{
value = null;
this.foobar = null;
this.NotifyPropertyChanged("Foobar");
}
}
}
}
Property is binded (MVVM) to Listview :
SelectedItem="{Binding Foobar, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
.
And when user changes value in list, selecteditem is changed and value is set in setter. I code is ok, when user selected incorrect value, to value and foobar null is assigned. But in WPF still selected value is displayed. When I set breakpoint in getter I can see that it return null too. How to refresh WPF to clear selected value in listview ? It shoud be empty like at the begin.
Thanks
The problem with your code is that you want to override value assigned by binding in your setter method. This will not work because control will not update on next property change for simple reason that it has invoked it by setting property. To implement validation on your values try this build in mechanism.
i would prefer using IDataErrorInfo and dont use property setter logic. the main advantage is that your property value in your viewmodel and your view are always the same and your viewmodel has the information wether the value is ok or not.

determine a textbox's previous value in its lost focused event? WPF

I have a textbox and have an onlostfocus event on it.
Inside the lostfocus method, is there a way I can determine if the user has actually changed the value in it?
i.e how do i get hold of any previous value in it?
Thanks
As with just about everything else in WPF, this is easier if you use data binding.
Bind the text box to a class property. By default, bindings update the source when the bound control loses focus, so you don't have to muck around with the LostFocus event. You then have access to both the new value and the value that the user entered in the property setter.
In the XAML it looks like this:
<TextBox Text="{Binding MyProperty, Mode=TwoWay}"/>
In the class it looks like this:
private string _MyProperty;
public string MyProperty
{
get { return _MyProperty; }
set
{
// at this point, value contains what the user just typed, and
// _MyProperty contains the property's previous value.
if (value != _MyProperty)
{
_MyProperty = value;
// assuming you've implemented INotifyPropertyChanged in the usual way...
OnPropertyChanged("MyProperty");
}
}
What comes to mind for me is a two stage approach. Handle the TextChanged event on the textbox and flag it. Then when the textbox OnLostFocus occurs you can simply check your flag to see if the text has been changed.
Here is a code snippet on how you could handle the tracking.
public class MyView
{
private bool _textChanged = false;
private String _oldValue = String.Empty;
TextChanged( ... )
{
// The user modifed the text, set our flag
_textChanged = true;
}
OnLostFocus( ... )
{
// Has the text changed?
if( _textChanged )
{
// Do work with _oldValue and the
// current value of the textbox
// Finished work save the new value as old
_oldValue = myTextBox.Text;
// Reset changed flag
_textChanged = false;
}
}
}
Store the original value somewhere. You could write a common component to store the value when it gets focus and compare the value when it loses focus. I've done this in ASP.NET and it works quite well.
Another way to solve this by databinding:
Bind the TextBox.Text to the property, that holds the inital value, but use a binding with
UpdateSourceTrigger=Explicit
Then, when the textbox loses focus, you can check the binding if source and target values differ, using this code snippet and evaluating the resulting BindingExpression:
BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
Some more code can be found here:
http://bea.stollnitz.com/blog/?p=41

Categories