My DatePickerTextBox is binded to a property of type DateTime? (which allows null)
I would like to set this property to null if the DatePickerTextBox is left empty.
My current approach:
private void TextChanged_Handler(object sender, TextChangedEventArgs e)
{
var dateTimePickerTextBox = (DatePickerTextBox)sender;
if (dateTimePickerTextBox.Text == string.Empty)
this.MyBindingObj.MyDate = null;
}
This works, but it has the disadvantage that the DatePickerTextBox UI is marked as red (error), because the binding between the text and DateTime was not successful. Although behind the scenes everything works fine.
I wonder if there is a cleaner way to do this.
I found a workaround, by simply adding dateTimePickerTextBox.Text = null; to the previous code:
private void TextChanged_Handler(object sender, TextChangedEventArgs e)
{
var dateTimePickerTextBox = (DatePickerTextBox)sender;
if (dateTimePickerTextBox.Text == string.Empty)
{
this.MyBindingObj.MyDate = null;
// Added this to avoid validation error:
dateTimePickerTextBox.Text = null;
}
}
Related
In App.xaml.cs I've registered a validation error event handler, to revert all my TextBox controls to previous value (value in VM property) when a validation error occurs (e.g. if a TextBox bound to a double property is entered a string value).
public App()
{
EventManager.RegisterClassHandler(typeof(TextBox), Validation.ErrorEvent,
new RoutedEventHandler(TextBox_ValidationErrorEventHandler));
}
private void TextBox_ValidationErrorEventHandler(object sender, RoutedEventArgs e)
{
var tb = (TextBox)sender;
DependencyProperty prop = TextBox.TextProperty;
BindingExpression binding = BindingOperations.GetBindingExpression(tb, prop);
if (binding != null) { binding.UpdateTarget(); }
}
This works fine. But the red line around the control remains and is never removed again when I enter legal values. How can I force update of the validation, so that the red line is removed?
You just need to "clean" the value of the Validation.ErrorTemplate property of the TextBox, by colling the SetErrorTemplate method:
private void TextBox_ValidationErrorEventHandler(object sender, RoutedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
DependencyProperty prop = TextBox.TextProperty;
BindingExpression binding = BindingOperations.GetBindingExpression(tb, prop);
if (binding != null)
{
binding.UpdateTarget();
Validation.SetErrorTemplate(tb, null);
}
}
}
I hope it can help you.
I am creating a little ASP.NET app and have a problem with one field value.
I have defined my enum in a class:
class Column
{
public enum Type {
Undefined = 0,
Integer = 1,
ShortDate = 2,
Etc = 3 }
// some other stuff
}
The app contains some controls to enter properties of a column, namely a dropdownlist for choosing the column type and some unimportant others. And when all properties are properly entered, SaveButton in enabled to save the column type info into a listbox. My Default.aspx.cs contains:
private Column.Type selectedType;
protected void Page_Load(object sender, EventArgs e)
{
// fill the ColumnTypeDropDownList (from the Column.Type enum)
if (!IsPostBack)
{
foreach (Column.Type ct in Enum.GetValues(typeof(Column.Type)))
{
ColumnTypeDropDownList.Items.Add(new ListItem(ct.ToString()));
}
}
}
protected void ColumnTypeDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
PrepareToSave();
}
// also called from other controls events, therefore in a separate method
private void PrepareToSave()
{
// control if all needed properties are entered and set the field
if ((ColumnNameTextBox.Text != "") && (ColumnTypeDropDownList.SelectedValue != Column.Type.Undefined.ToString()))
{
foreach (Column.Type ct in Enum.GetValues(typeof(Column.Type)))
{
if (ct.ToString() == ColumnTypeDropDownList.SelectedValue) selectedType = ct;
}
SaveButton.Enabled = true;
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
ColumnsListBox.Items.Add(selectedType.ToString()); // always writes "Undefined"
}
The problem is that it always writes "Undefined" into the listbox, even though another type was selected from the dropdownlist. I tried to add the item into the listbox inside the PrepareToSave() method and that works correctly, but I need it outside. On the other hand, the condition controlling if any other value than Undefined is selected from the dropdownlist works well. It seems that the field selectedType has the correct selected value only inside the PrepareToSave() method.
AutoPostBack of all the controls is enabled.
Am I missing something about the enums or do you have any tips how to fix it? Thanks.
Your problem is in the line...
ColumnTypeDropDownList.Items.Add(new ListItem(ct.ToString()));
..namely in new ListItem(ct.ToString()). When you use this constructor of the ListItem class, you create an item with Value set to null. Later you compare against the value:
if (ct.ToString() == ColumnTypeDropDownList.SelectedValue) selectedType = ct;
Since Value of each of the items is null, ColumnTypeDropDownList.SelectedValue is also null and your comparison fails. That should be also easily figured out in a debugger.
The correct list item constructor for you is
ListItem listItem = new ListItem(ct.ToString(), ct.ToString());
As an additional issue, you have to call PrepareToSave in SaveButton_Click, since the selectedType field will have lost its value across requests. PrepareToSave will rebuild that value.
That's most probably because of your if condition as pointed below
if ((ColumnNameTextBox.Text != "") && (ColumnTypeDropDownList.SelectedValue != Column.Type.Undefined.ToString()))
{
Instead of ColumnNameTextBox.Text != "" use !string.IsNullOrEmpty(ColumnNameTextBox.Text)
Just another tip:
Use GetNames instead of GetValues in your foreach loop:
foreach (var ct in Enum.GetNames(typeof(Column.Type)))
{
//do your stuff.
}
If you want to use AutoPostBack ...
Add a hidden control to your page.
In your PrepareToSave(); method you just can add the selectetType like yourControlName.Text = ct;
And change your save handler to this ....
protected void SaveButton_Click(object sender, EventArgs e)
{
// Read the value of the hidden control
ColumnsListBox.Items.Add(yourControlName.Text);
}
I have a wpf project where I have a bound text box that has AllowDrop set to true. If I type directly into this text box and leave the box, the bound property for this text box changes as expected.
However, if I create a drop event and set the text value for the text box to the filename value, the text box bound property does not change. I have to click into the text box and tab out of it.
I must be misunderstanding how bound properties should work. My thinking was that if the text of the box changes that it should update the bound property as well.
As it stands now, I have to have the code behind update the property rather than rely upon the binding. Below is from a sample project I created.
XAML:
<Window.DataContext>
<local:XmlFile x:Name="XmlFileInfo"/>
</Window.DataContext>
...
<StackPanel Orientation="Horizontal" Grid.Row="0">
<Label Content="XML File:"/>
<TextBox x:Name="xmlFilePath" Text="{Binding XMLFile}" Height="25" VerticalAlignment="Top" MinWidth="300" AllowDrop="True" PreviewDragOver="xmlFilePath_PreviewDragOver" Drop="xmlFilePath_Drop"/>
</StackPanel>
And below is my viewmodel
public class XmlFile
{
private string _xmlfile;
private string _xmlElementName;
public string XMLFile
{
get { return _xmlfile; }
set
{
if (value != _xmlfile)
{
_xmlfile = value;
_xmlElementName = SetElementNameFromFileName();
}
}
}
...
}
And finally my code behind for XAML
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Exit_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
private void SetControlState()
{
FileTest.IsEnabled = false;
if (!string.IsNullOrEmpty(XmlFileInfo.XMLFile))
{
if(XmlFileInfo.IsValidXml(XmlFileInfo.XMLFile))
{
FileTest.IsEnabled = true;
}
}
}
private void xmlFilePath_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void xmlFilePath_Drop(object sender, DragEventArgs e)
{
var filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
if (filenames == null) return;
var filename = filenames.FirstOrDefault();
if (filename == null) return;
//XmlFileInfo.XMLFile = filename; <-- This bypasses the binding
(sender as TextBox).Text = filename; // This should trigger updating binding
SetControlState(); //<-- enables a button control if file is valid
}
}
I have tried setting the binding mode to twoway, and other binding settings without any change in behavior. What I would like to do is figure out how to get the drop functionality to act just like manually typing into the box and leaving the box without having to bypass my binding and directly setting the property.
Make your ViewModel implement INotifyPropertyChanged.
Instead of directly changing the text of your textbox change the fileName in your viewModel. That will do the trick.
You also have to call OnPropertyChanged in the setter of your XMLFile-Property
public class XmlFile : INotifyPropertyChanged
{
private string _xmlfile;
private string _xmlElementName;
public string XMLFile
{
get { return _xmlfile; }
set
{
if (value != _xmlfile)
{
_xmlfile = value;
_xmlElementName = SetElementNameFromFileName();
OnPropertyChanged(nameof(XMLFile); //this tells your UI to update
}
}
}
...
}
private void xmlFilePath_Drop(object sender, DragEventArgs e)
{
var filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
if (filenames == null) return;
var filename = filenames.FirstOrDefault();
if (filename == null) return;
//XmlFileInfo.XMLFile = filename; <-- This bypasses the binding
var viewModel = (XmlFile)this.DataContext;
viewModel.XMLFile = filename;
SetControlState(); //<-- enables a button control if file is valid
}
You should have a look at how DataBinding works.
What you're looking for is the following:
var textBox = sender as TextBox;
if (textBox == null)
return;
// Sets the value of the DependencyProperty without overwriting the value, this will also update the ViewModel/XmlFile that TextBox is bound to.
textBox.SetCurrentValue(TextBox.TextProperty, "My Test Value");
You can also force the binding to update the target(XmlFile) by the following:
var textBinding = textBox.GetBindingExpression(TextBox.TextProperty);
// This will force update the target (rather than wait until control loses focus)
textBinding?.UpdateTarget();
I had a situation where I needed to populate multiple textboxes with filepaths. I used the ideas from akjoshi presented here: Update ViewModel from View. I implemented it in the following way:
private void Text_Drop(object sender, DragEventArgs e)
{
TextBox temp = sender as TextBox;
if(temp == null)
return;
string[] tempArray = (string[])e.Data.GetData(DataFormats.FileDrop, false);
temp.Text = tempArray[0];
sender = temp;
BindingExpression bind = BindingOperations.GetBindingExpression(temp, TextBox.TextProperty);
bind.UpdateSource();
}
Hope this helps!
public void DD_Location()
{
var ctx = new LCCDB_EF();
var query = ctx.tbl_Location;
CB_Location.DataContext = query.ToList();
}
private void CB_Location_DropDownClosed(object sender, EventArgs e)
{
textbox_test.Text =CB_Location.Text;
}
Output in Textbox
System.Data.Entity.DynamicProxies.Location_5E43C6C196972BF0754973E48C9C941092D86818CD94005E9A759B70BF6E48E6
Try this
if(null != CB_Location.SelectedItem)
textbox_test.Text = CB_Location.SelectedItem.ToString();
Without seeing your XAML I can't be sure, but are you sure you've bound the list correctly? Try setting the Items property of combobox to your list, rather than the data context. Depending on what the type is and what you'd like to bind the text to, you may need to set the DisplayMemberPath property as appropriate, too.
This is selection changed event :
private void cbUsers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectedUser = (sender as ComboBox).SelectedItem.ToString();
GetUserInformation();
}
GetUserInformation is just selecting password from database. Users are deleted from the database and then the following refreshes the ComboBox items:
public void FillComboBox()
{
cbUsers.ItemsSource = null;
HProDataContext db = new HProDataContext();
var _UserName = (from d in db.users select d.username).ToList();
cbUsers.ItemsSource = _UserName;
}
HProDataContext db = new HProDataContext();
var _UserID = (from d in db.users where d.username == cbUsers.Text select d.id).SingleOrDefault();
user u = db.users.Single(p => p.id == _UserID);
db.users.DeleteOnSubmit(u);
db.SubmitChanges();
cbUsers.ItemsSource = null;
cbUsers.Text = null;
FillComboBox();
When using this last method it gives such error:
Object reference not set to an instance of an object.
The error falls on this line of the FillComboBox method:
SelectedUser = (sender as ComboBox).SelectedItem.ToString();
Does anyone have an idea as to what is wrong?
I'd guess that SelectedItem is null and therefore you're calling ToString on nothing.
Consider trying this:
if ((sender as ComboBox).SelectedItem != null)
{
SelectedUser = (sender as ComboBox).SelectedItem.ToString();
}
However, doesn't your ComboBox have an identifier? This can allow you to refrain from unnecessary conversions with as:
if (myComboBox.SelectedItem != null)
{
SelectedUser = myComboBox.SelectedItem.ToString();
}
Your event handler is probably getting called when there is no SelectedItem.
I would write this as:
private void cbUsers_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var usersComboBox = (ComboBox)sender;
if (usersComboBox.SelectedItem == null) return;
SelectedUser = usersComboBox.SelectedItem.ToString();
GetUserInformation();
}
Here you are expecting that sender is always a ComboBox, so I would use a cast instead of as. The exception will let you know if your assumption is wrong.
This will just skip your code if the event handler is called without a selected item.
One possibility is that sender might not be a ComboBox.
A problem of using an as operator without a null check is you get a NullReferenceException instead of a InvalidCastException which is more appropriate
Use an explicit cast instead e.g. ((ComboBox)sender).SelectedItem.ToString(); if you are sure sender is a ComboBox; otherwise check for null before usage.