I have an ItemsContol bound to a Country model - which look like this.
Country
--int Id
--string Name
--List Counties
In the DataTemplate of the ItemsControl there's a Listbox - which is bound to the Counties property.
So what I want is only one item in any of the listboxes be selected at any one time.
For example:
I have an item selected in the first listbox and I click an item in the second listbox, then the first listbox shouldn't have any selected items.
Any ideas ??
Add a SelectedCounty property to your Country object. Then you can bind the SelectedItem on your ListBox to that property. Then in code manually set all others to null. Something like so
Country.OnPropertyChanged += (s,e) =>
{
if(e.PropertyName == "SelectedCounty")
{
foreach(Country country in MyCountries)
if(country != sender)
country.SelectedCounty = null;
}
}
Just for reference here's the solution I'm using - it resides in the CountryViewModel
private CountyModel _selectedcounty;
public CountyModel SelectedCounty
{
get { return _selectedcounty; }
set
{
_selectedcounty = value;
RaisePropertyChanged("SelectedCounty");
if (value != null)
{
if (CountySelectedEvent != null)
CountySelectedEvent(value, EventArgs.Empty);
Messenger.Default.Send<CountyModel>(value, "SelectedCounty");
}
}
}
public CountryViewModel()
{
Counties = new ObservableCollection<CountyModel>();
Messenger.Default.Register<CountyModel>(this, "SelectedCounty",
msg =>
{
if(msg != this.SelectedCounty && msg != null)
this.SelectedCounty = null;
});
}
Hope it helps someone :)
Related
Below is the code of binding data into dropdown list of the property grid.
[Category(Constants.ColumnScrambler), PropertyOrder(2)]
[DisplayName(Constants.Condition)]
[Description(Constants.Condition)]
[ItemsSource(typeof(CustomColumnConditionProperty))]
public ConditionType Condition
{
get
{
return this.condition;
}
set
{
this.condition = value;
if (condition == ConditionType.Expression)
{
var descriptor = TypeDescriptor.GetProperties(this.GetType())["ConditionExpressionType"];
var attrib = (BrowsableAttribute)descriptor.Attributes[typeof(BrowsableAttribute)];
FieldInfo isBrow = attrib.GetType().GetField("browsable", BindingFlags.NonPublic | BindingFlags.Instance);
if (isBrow != null) isBrow.SetValue(attrib, true);
descriptor.SetValue(this, attrib);
}
this.OnPropertyChanged("Condition");
this.objExpressionBuilder.NotifyPropertyCheckSelected(this);
}
}
I want to show below Textbox when i select Expression from the dropdown list of property grid.
[Category(Constants.ColumnScrambler)]
[DisplayName("Expression")]
[Description("Expression")]
[Browsable(false)]
public string ConditionExpressionType
{
get { return "TestData"; }
}
But the problem is that to do so ,i have to rebind the property grid again and all the values vanished. can anyone please suggest a way without rebind on selection of dropdown list in property grid i can show the text box.
I have a WPF Application with two ComboBox
When I select the first one the items related to the first combobox will be populated on the second one
Here is my select Property
public string SelectedApplication
{
set
{
if (_selectedApplication == value) return;
this._selectedApplication = value;
InitializeTransactionTypes();
}
get
{
return this._selectedApplication;
}
}
here i am checking a matching id between the two comboboxes to populate the second combobox items.
ObservableCollection<TransactionTypeViewModel> _transTypeObsList = new ObservableCollection<TransactionTypeViewModel>();
private void InitializeTransactionTypes()
{
if (_selectedApplication != null)
{
var getAppCode =
ApplicationVModel.GetAllApplications()
.FirstOrDefault(apps => apps.Name == _selectedApplication);
var transTypeList = TransactionTypeVModel.GetAllViewModelTransTypes()
.Where(t => getAppCode != null && t.Id == getAppCode.Id);
transactionTypes = new ObservableCollection<TransactionTypeViewModel>(transTypeList);
NotifyPropertyChanged("TransactionTypes");
}
}
More information about methods:
List of VM mapped from List of Model
public List<TransactionTypeViewModel> GetAllViewModelTransTypes()
{
TransactionTypeViewModels = TransactionTypeModel.GetAllTransactionTypes().Select(transType => new TransactionTypeViewModel
{
Id = transType.Id,
Name = transType.Name,
})
.ToList();
return TransactionTypeViewModels;
}
Lets say I select first combobox has {A,B,C,D} ...and the second combobox has {A'1,A'2,A'3}, when I select item from first Combobox the second combobo keeps populating
items. I wanted to show only {A'1 for A} {B'1 for B} ...etc but now what it does is {A'1 A'1 A'1 ..... for A} {B'1 B'1 B'1 ....for B} for every select.
I want the previous selection to be cleared and display a new list per select. Thanks
To sum up comments. Instead of creating new ObservableCollection every time something changes reuse one that you already have and it will notify UI about changes. Your InitializeTransactionTypes should look something like this:
private void InitializeTransactionTypes()
{
if (_selectedApplication != null)
{
var getAppCode =
ApplicationVModel.GetAllApplications()
.FirstOrDefault(apps => apps.Name == _selectedApplication);
transactionTypes.Clear();
foreach(var transactionItem in TransactionTypeVModel.GetAllViewModelTransTypes().Where(t => getAppCode != null && t.Id == getAppCode.Id))
transactionTypes.Add(transactionItem);
}
}
and like this you should not have to notify about TransactionTypes changes any more
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
I have a combobox of type List. I have the ItemsSource and the ItemSelected bound through the datacontext. If the selected item has been changed then I show a pop up message confirming the users action. On clicking of 'Ok' the selection gets changed. But on clicking of cancel, the selection should be cancelled and previous item should be retained. Below is the property that is bound to SelectedItem of the combobox.
Public SomeClass Sel
{
get
{
return _sel;
}
set
{
if (_sel != value)
{
var sview = _sel;
if (Compare())
{
_sel = value;
if (Sel != null)
IsDefault = Sel.IsDefault;
OnPropertyChanged(() => Sel);
}
else
{
MessageBoxResult result = MessageBox.Show("Message.", "Owb Message", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
_sel = value;
if (Sel != null)
IsDefault = Sel.IsDefault;
OnPropertyChanged(() => Sel);
}
else
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_sel = sview;
OnPropertyChanged("Sel");
}), DispatcherPriority.Send, null);
return;
}
}
}
}
}
The combo box is in a pop window. So would Dispatcher object work in that case?
I'm guessing the selected value is retained, but the View doesn't update correctly.
Have a look at this article: http://www.codeproject.com/Articles/407550/The-Perils-of-Canceling-WPF-ComboBox-Selection. Basically, the few workarounds that did exist in .Net 3.5 no longer work in .Net 4.0..
As a general rule, if you've got visual controls leaking into your viewmodel, you're going down a path you don't want to go down.
Create a Behavior that intercepts the OnChanged event of the ComboBox and launches a message box. Here's a tutorial on using behaviours
This keeps all the UI logic in the UI and leaves your viewmodel to manage data and validation.
It works like magic now! I missed out setting the value before calling dispatcher.
_sel = sview
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?