How to reload the ICollectionView binding to wpf combobox? - wpf

I have one textBox and one combobox in wpf usercontrol. ComboxBox is binded to ICollectionView (CurencyList) which populates Currency Pairs like GBP/EUR,CLP/EUR,USD/EUR,EUR/USD etc. Whenever the user writes in textbox e.g. EUR the combobox should get filtered and display the dropdownlist with EUR as the second currency.
For this I have used like:
public string Currency
{
get {
return _criteriaType.currency; }
set
{
if (_criteriaType.currency != value)
{
_criteriaType.currency = value.EmptyOrWhiteSpaceAsNull();
base.OnPropertyChanged("Currency");
CurrencyList.Filter = new Predicate<object>(Contains);
}
}
}
public bool Contains(object de)
{
CurrencyPair o = de as CurrencyPair;
if (Currency != null || Currency == string.Empty)
{
return (o.name.Substring(3, 4).ToLower().Contains(Currency.ToLower()));
}
else
{
IsOpen = false;
OnPropertyChanged("IsOpen");
return false;
}
}
CurrencyList is coming from a webservice:
private ICollectionView GetCurrencyPair()
{
strCurrencyPair.arg0 = (Currency != string.Empty && Currency != null) ? Convert.ToString(Currency).ToUpper() : string.Empty;
string[][] cPair = ServiceLocator.Resolve<IWebServiceRepository>().BusinessWebService.getCurrenyPairs(strCurrencyPair);
foreach (string[] item in cPair)
{
IList.Add(new CurrencyPair() { name = (Convert.ToString(item[0]).ToUpper() + "/" + Convert.ToString(item[1]).ToUpper()) });
}
return CurrencyList =CollectionViewSource.GetDefaultView(IList);
}
Filtering is working fine. But when the user deletes the currency from textbox with backward arrow key from keyboard, the combobox filtered to nothing i.e. dropdownlist is empty.
How to overcome this problem. kindly suggest?

you need to update your collection view through the text change event
private void OnTextChanged(object sender, Eventargs e) {
var vm = yourViewModelOrWhatEver;
((ICollectionView)vm.CurrencyList).Refresh();
}
or set the filter predicate once and fire only the refresh on currency change
public void ctor() {
CurrencyList.Filter = new Predicate<object>(Contains);
}
public string Currency {
get { return _criteriaType.currency; }
set {
if (_criteriaType.currency == value) {
return;
}
_criteriaType.currency = value.EmptyOrWhiteSpaceAsNull();
base.OnPropertyChanged("Currency");
CurrencyList.Refresh(); // refresh/filter the collection view
}
}
hope that helps
EDIT
you say
Filtering is working fine. But when the user deletes the currency from
textbox with backward arrow key from keyboard, the combobox filtered
to nothing i.e. dropdownlist is empty. How to overcome this problem.
kindly suggest?
then you must change your predicate function to this one
public bool Contains(object de)
{
CurrencyPair o = de as CurrencyPair;
if (Currency != null || Currency == string.Empty) {
// Currency == string.Empty should also true
return (Currency == string.Empty) || (o.name.Substring(3, 4).ToLower().Contains(Currency.ToLower()));
} else {
IsOpen = false;
OnPropertyChanged("IsOpen");
return false;
}
}

I would assume the issue lies with this statement: string[][] cPair =
ServiceLocator.Resolve().BusinessWebService.getCurrenyPairs(strCurrencyPair);
what is the return value from that service if you pass string.empty for strCurrencyPair as you will currently be doing once the textbox is empty from backspacing?

Related

Vaadin-Unable to update grid container on combobox value change

I am using vaadin 7.7.7
In a grid i have a combobox as an edited item in one of the columns
as
grid.addColumn("columnProperty").setEditorField(combobox);
I need to update a property/cell in same row based on the combobox selection change
My issue is , the selection change event triggers twice, once when the combobox in clicked and second when the selection value is changed. But the updated value in next cell gets reflected on UI only first time.
Below is the code written . Any solutions?
Combobox.addValueChangeListener(new ValueChangeListener()
#Override
public void valueChange(ValueChangeEvent event) {
// below line works only first time when the combobox is clicked,but i want
//it when the item in the combobox is changed
gridContainer.getContainerProperty(editedRow,"editedColumProperty").setValue("ValueTobeUpdated");}
});
Need to update the unit column on combobox change in edited mode(before saving)
Refer below link for image
example image
You will get value change events even when the field gets value that it should show to the user. In order to get event that indicates that the user has accepted the input you should use field group (setEditorFieldGroup).
From Book of Vaadin example for grid editing:
grid.getColumn("name").setEditorField(nameEditor);
FieldGroup fieldGroup = new FieldGroup();
grid.setEditorFieldGroup(fieldGroup);
fieldGroup.addCommitHandler(new CommitHandler() {
private static final long serialVersionUID = -8378742499490422335L;
#Override
public void preCommit(CommitEvent commitEvent)
throws CommitException {
}
#Override
public void postCommit(CommitEvent commitEvent)
throws CommitException {
Notification.show("Saved successfully");
}
});
Edit
I assume that you want to connect Parameter and Unit comboboxes. I would do that with this kind of value change lister
BeanItemContainer container = new BeanItemContainer<>(
Measurement.class,
measurements);
Grid grid = new Grid(container);
grid.setEditorEnabled(true);
ComboBox parameterComboBox = new ComboBox();
ComboBox unitComboBox = new ComboBox();
parameterComboBox.addItems(Parameter.Pressure, Parameter.Temperature, Parameter.Time);
parameterComboBox.addValueChangeListener(v -> setUnits(parameterComboBox, unitComboBox));
grid.getColumn("parameter").setEditorField(parameterComboBox);
grid.getColumn("unit").setEditorField(unitComboBox);
Units could be updated like this. I think you need to preserve current value and set it back if you replace available items in the combobox.
private void setUnits(ComboBox parameterComboBox, ComboBox unitComboBox) {
Object currentValue = unitComboBox.getValue();
List<String> units = unitsForParameter(parameterComboBox.getValue());
unitComboBox.removeAllItems();
unitComboBox.addItems(units);
if (units.contains(currentValue)) {
unitComboBox.setValue(currentValue);
} else {
unitComboBox.setValue(null);
}
}
private List<String> unitsForParameter(Object value) {
if (value == null) {
return Collections.emptyList();
} else if (value == Parameter.Pressure) {
return asList("Pascal", "Bar");
} else if (value == Parameter.Temperature) {
return asList("Celcius", "Kelvin");
} else if (value == Parameter.Time) {
return asList("Second", "Minute");
} else {
throw new IllegalArgumentException("Unhandled value: " + value);
}
}

Searching In DataGrid

I want to search in a data grid via typing in a textbox, but I am unable to find solution.
Do I need to do any binding? If so, then how do I do it?
If you want filter text in your Datagrid i.e by Name, try this...
private bool DataMatchesFilterText(User user, string filterText)
{
return user.Name.ToString() == filterText;
}
Yeah you will require your data grid to be bound to a Property that contains all your data.
Then add a event handler to your Textbox to act on one of the key events, e.g.
Xaml:
<TextBox x:Name="SearchBox" KeyUp="FilterTextBox_TextChanged" />
Then in the code behind you need to act on that event. Here you need to extract the filter text, get the rows in your DataGrid and then perform some method to determine if it should be visible or not. You will need to implement your own DataMatchesFilterText method.
Codebehind:
private void FilterTextBox_TextChanged(object sender, KeyEventArgs e)
{
var filterTextBox = (TextBox)sender;
var filterText = filterTextBox.Text;
SetRowVisibilityByFilterText(filterText);
}
private void SetRowVisibilityByFilterText(string filterText)
{
GetVisibleRows(yourGrid)
.ToList()
.ForEach(
x =>
{
if (x == null) return;
x.Visibility =
DataMatchesFilterText(x.Item as YourRowProperty, filterText) ? Visibility.Visible : Visibility.Collapsed;
});
}
public static IEnumerable<DataGridRow> GetVisibleRows(DataGrid grid)
{
if (grid == null || grid.Items == null) yield break;
int count = grid.ItemsSource == null
? grid.Items.Count
: grid.ItemsSource.Cast<object>().Count();
for (int i = 0; i < count; i++)
{
yield return (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(i);
}
}

Can you replace DatePickerTextBox with a different TextBox in DatePicker and still get the validation functionality?

I replaced the DatePickerTextBox in the control template of DatePicker, and it no longer ensures a valid date value for the text entered. I tested different strings with a plain DatePickerTextBox, too, and it didn't validate them, so it must be some kind of interaction between DatePickerTextBox and DatePicker. Is there a way to get that interaction with a different TextBox control in the control template that isn't DatePickerTextBox, or will I have to recreate that validation in my own custom control?
Short explanation with answer:
In the OnApplyTemplate method of a custom TextBox control, I added:
public override void OnApplyTemplate()
{
LostFocus += OnLostFocus;
base.OnApplyTemplate();
}
And then the method:
private void OnLostFocus(object sender, RoutedEventArgs e)
{
if (!Text.Equals(oldText))
{
DateTime d;
if (!DateTime.TryParse(Text, out d))
{
Text = oldText;
}
}
oldText = Text;
}
Long explanation of why it doesn't work in the control template if you replace DatePickerTextBox:
I poked around in the reference source and discovered the following:
In the OnApplyTemplate method for DatePicker, some handlers are added, specifically for a DatePickerTextBox, which it will not apply if it doesn't find one (which explains why DatePickerTextBox doesn't validate on its own, either - DatePicker adds the handlers, not DatePickerTextBox):
_textBox = GetTemplateChild(ElementTextBox) as DatePickerTextBox;
if (this.SelectedDate == null)
{
SetWaterMarkText();
}
if (_textBox != null)
{
_textBox.AddHandler(TextBox.KeyDownEvent, new KeyEventHandler(TextBox_KeyDown), true);
_textBox.AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(TextBox_TextChanged), true);
_textBox.AddHandler(TextBox.LostFocusEvent, new RoutedEventHandler(TextBox_LostFocus), true);
if (this.SelectedDate == null)
{
if (!string.IsNullOrEmpty(this._defaultText))
{
_textBox.Text = this._defaultText;
SetSelectedDate();
}
}
else
{
_textBox.Text = this.DateTimeToString((DateTime)this.SelectedDate);
}
}
So, tracking it down(here comes the fun part)...
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedDate();
}
Then:
private void SetSelectedDate()
{
if (this._textBox != null)
{
if (!string.IsNullOrEmpty(this._textBox.Text))
{
string s = this._textBox.Text;
if (this.SelectedDate != null)
{
// If the string value of the SelectedDate and the TextBox string value are equal,
// we do not parse the string again
// if we do an extra parse, we lose data in M/d/yy format
// ex: SelectedDate = DateTime(1008,12,19) but when "12/19/08" is parsed it is interpreted as DateTime(2008,12,19)
string selectedDate = DateTimeToString(this.SelectedDate.Value);
if (string.Compare(selectedDate, s, StringComparison.Ordinal) == 0)
{
return;
}
}
DateTime? d = SetTextBoxValue(s);
if (!this.SelectedDate.Equals(d))
{
this.SetCurrentValueInternal(SelectedDateProperty, d);
this.SetCurrentValueInternal(DisplayDateProperty, d);
}
}
else
{
if (this.SelectedDate.HasValue)
{
this.SetCurrentValueInternal(SelectedDateProperty, (DateTime?)null);
}
}
}
else
{
DateTime? d = SetTextBoxValue(_defaultText);
if (!this.SelectedDate.Equals(d))
{
this.SetCurrentValueInternal(SelectedDateProperty, d);
}
}
}
The important thing there is the call to SetTextBoxValue:
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return this.SelectedDate;
}
else
{
DateTime? d = ParseText(s);
if (d != null)
{
SafeSetText(this.DateTimeToString((DateTime)d));
return d;
}
else
{
// If parse error:
// TextBox should have the latest valid selecteddate value:
if (this.SelectedDate != null)
{
string newtext = this.DateTimeToString((DateTime)this.SelectedDate);
SafeSetText(newtext);
return this.SelectedDate;
}
else
{
SetWaterMarkText();
return null;
}
}
}
}
And the important thing there is the call to ParseText:
private DateTime? ParseText(string text)
{
DateTime newSelectedDate;
// TryParse is not used in order to be able to pass the exception to the TextParseError event
try
{
newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)));
if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate))
{
return newSelectedDate;
}
else
{
DatePickerDateValidationErrorEventArgs dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue)), text);
OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException)
{
throw dateValidationError.Exception;
}
}
}
catch (FormatException ex)
{
DatePickerDateValidationErrorEventArgs textParseError = new DatePickerDateValidationErrorEventArgs(ex, text);
OnDateValidationError(textParseError);
if (textParseError.ThrowException && textParseError.Exception != null)
{
throw textParseError.Exception;
}
}
return null;
}
The last two methods have the validation I was looking for. If ParseText returns null, then SetTextBoxValue reverts the value in the text box back to its previous value. Because DatePicker is the control that adds the handler, you won't get the validation outside of DatePicker. Since DatePicker looks specifically for a DatePickerTextBox named "PART_TextBox" (that is what the ElementTextBox string is), a DatePickerTextBox must be used, or the handler will not be applied (since _textbox will be null).

Popup and ComboBox binding with Silverlight

I have a problem regarding the comportment my ComboBox.
First I use a combobox to display all elements in a IEnumarale.
Then, with a button wich open a popup, the user can add an alement to that list.
The problem is that when the user validate his choice and close the popup, the element is not automatly added to the ComboBox without doing a refresh of the page.
The combobox is coded as follows :
<telerik:RadComboBox x:Name="MyElements"
SelectionChanged="MyElements_OnSelectionChanged"
ItemTemplate="{StaticResource ComboBoxElementsTemplate}"
ItemsSource="{Binding ListElements}"/>
The constructor of the list is :
public IEnumerable<Element> ListElements
{
get { return _listElements; }
set
{
_listElements= value;
RaisePropertyChange("ListElements");
}
}
And the code behind of the button to validate the user choice in the popup :
private ObservableCollection<HistoriqueElement> elementList = null;
private void SelectClick(object sender, RoutedEventArgs e)
{
var element= _GridList.SelectedItem as HistoriquePasserelle;
if (_GridList.SelectedItem != null)
{
var installation = this.DataContext as Installation;
if (installation != null && element!= null)
{
element.DateFin = DateTime.Now;
HistoriqueElement newElement= new HistoriqueElement()
{
Installation = installation,
ContactAction = GlobalActeurs.Current.CurrentContact,
Date = DateTime.Now,
Element = element.Element,
StatutElement = element.StatutElement ,
Owner= element.Owner,
};
elementList.Remove(element);
}
MainPage.ClosePopup();
}
}
When the user choose a new element in the list display in the popup and validate his choice, he returns to the main page, but his choice is not automatically added to the combobox.
I can post you any parts of the code.
Thank you in advance.
The method OnDataContextChanged :
public override void OnDataContextChanged(DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is Installation)
{
if (MainPage.CurrentInstallation.LastElements != null)
{
ListElements = MainPage.CurrentInstallation.LastElements;
MyElements.SelectedIndex = 0;
}
else
{
LoadOperation<Element> operation =
_context.Load(_context.GetCurrentElementsByInstallationId(MainPage.CurrentInstallation.Id));
this._busy.IsBusy = true;
operation.Completed += delegate
{
this._busy.IsBusy = false;
if (operation.ManageError())
{
ListElements = operation.Entities;
}
};
}
this.DataContext = this;
}
else
{
RaisePageTitleChanged();
if (MainPage.CurrentInstallation == null)
return;
}
if (MyElements.SelectedItem == null && MyElements.Items.Any())
{
MyElements.SelectedIndex = 0;
}
}
If the collection the ItemsSource is bound to implement INotifyCollection changed, that is, it's an ObservableCollection<>, then the combobox will be notified of any changes to the collection and you will not need to rebind or refresh, it will all be automatic.
Once you add the item to the list, bind the itemsource to the combobox, then you dont have to refersh.
MyElements.ItemsSource = ListElements

Is there a nullable datepicker that I can bind to?

I am looking for a datepicker like what microsoft provides, but it doesn't support null values, and since this is tied to a nullable field on a database that isn't acceptable.
I found this one, but according to the comments at the bottom of the page it has issues with binding to a database. I also have one in my project that I inherited, but it has similar issues (sometimes it shows values, sometimes it doesn't). Does anyone know of one that works?
Use a date picker to populate a textbox and if they want the field to be null, just erase the contents of the textbox (and then handle the blank input accordingly).
This also provides the added benefit of allowing the user to type in their date if they so choose.
Smart FieldPackEditor has a datepicker that is nullable. I believe it does everything that you need. I wish this was around when I was dealing with this sort of stuff. I still remember all the workarounds I had to implement with Microsoft's datepicker control. Uggh!
http://www.visualhint.com/index.php/fieldpackeditor/
why not use a client side datepicker to populate a text field. If the textfield is empty, then you have a null date, otherwise convert the value.
jQuery has a nice easy to use datepicker. http://jqueryui.com
This one seems to work, one of my co-workers had it:
using System;
using System.Windows.Forms;
namespace CustomControls
{
public class NullableBindableDateTimePicker : System.Windows.Forms.DateTimePicker
{
private Boolean isNull = false;
private DateTimePickerFormat baseFormat = DateTimePickerFormat.Short;
private Boolean ignoreBindOnFormat = false;
public NullableBindableDateTimePicker()
{
this.Format = baseFormat;
if (baseFormat == DateTimePickerFormat.Custom) this.CustomFormat = " ";
}
public Boolean IsNull
{
get { return isNull; }
set
{
isNull = value;
this.Checked = value;
}
}
//TODO: Add BaseCustomFormat
public DateTimePickerFormat BaseFormat
{
get { return baseFormat; }
set { baseFormat = value; }
}
public object BindMe
{
get
{
if (IsNull) return System.DBNull.Value;
else return base.Value;
}
set
{
//String s = this.Name;
if (ignoreBindOnFormat) return;
if (System.Convert.IsDBNull(value))
{
// for some reason setting base.format in this.format calls set BindMe.
// we need to ignore the following call
ignoreBindOnFormat = true;
this.Format = DateTimePickerFormat.Custom;
ignoreBindOnFormat = false;
this.CustomFormat = " ";
IsNull = true;
}
else
{
ignoreBindOnFormat = true;
this.Format = baseFormat;
ignoreBindOnFormat = false;
if (baseFormat == DateTimePickerFormat.Custom) this.CustomFormat = " ";
IsNull = false;
base.Value = (DateTime)value;
}
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.Delete)
{
this.BindMe = DBNull.Value;
}
}
protected override void OnCloseUp(EventArgs eventargs)
{
base.OnCloseUp(eventargs);
BindMe = base.Value;
}
}
}

Resources