I am populating my WPF ComboBox like this
foreach (Function fx in XEGFunctions.GetAll())
{
ComboBoxItem item = new ComboBoxItem();
item.Content = fx.Name;
item.ToolTip = fx.Signature;
//item.( some property ) = fx.FunctionValue;
cmbBoxTransformation.Items.Add(item);
}
cmbBoxTransformation.SelectedIndex = 0;
How can I set some different value to each ComboBoxItem.
If the value you're looking to set is only used in the back end, and not displayed to the user, the Tag property is probably your best bet.
item.Tag = fx.FunctionValue;
Two options
You can create derived type from ComboBoxItem, and define properties in derived type.
You can create arbitrary collection of item (with your custom properties), and set ComboBox.ItemsSource to that collection, and DisplayMemberPath to the field that needs to be displayed in the Combobox.
Binding combobox to display source and binding source
How SelectedValue and DisplayMemberPath saved my life
this little tick may helps someone
<ComboBox SelectedIndex="1" SelectedValuePath="Tag" SelectedValue="{Binding SampleDept,Mode=OneWayToSource}" >
<ComboBoxItem Content="8-bit" Tag="8" ></ComboBoxItem>
<ComboBoxItem Content="16-bit" Tag="16" ></ComboBoxItem>
<ComboBoxItem Content="24-bit" Tag="24"></ComboBoxItem>
<ComboBoxItem Content="32-bit" Tag="32"></ComboBoxItem>
</ComboBox>
public class SampleModel{
public int SampleDept{
get { return _sampleDept; }
set {
_sampleDept = value;
OnPropertyChanged("SampleDept");
}
}
}
var listItems = val.Split('$');
DataTemplate dt = new DataTemplate();
var combo = new FrameworkElementFactory(typeof(ComboBox));
combo.SetValue(ComboBox.ItemsSourceProperty, listItems);
combo.SetValue(ComboBox.SelectedValueProperty, "Whatever");
combo.SetBinding(ComboBox.SelectedValueProperty, new Binding("Value") { Source = mkvc });
dt.VisualTree = combo;
dt.Seal();
add this to editor template of whatever you want to add combobox to it => mkvc is a class for holding my data
PropertyDefinition pd = new PropertyDefinition();
pd.EditorTemplate = dt;
//rpg =>radPropertyGrid
rpg.PropertyDefinitions.Add(pd);
rpg.Item = propertyList;
propertylist is list of myclass
Related
Im not sure if the title represent the problem I have. The explanation here below should be clearer.
I cannot get my code to select the correct value in the ComboBox with the data from my List. I checked for similar problems here in SO but after reading several I cannot find one that matches my problem.
I fill the list with the follwing code:
if (roleComboBox.SelectionBoxItem.Equals("Player"))
{
memberID++;
Player player = new Player(memberID, team, firstName, lastName, age, salary, yearsActive, position, minutesPerGame);
players.Add(player);
PrintList();
}
Now I want to retrieve the data from the List and put it back into the TextBox/ComboBox it came from so I can change the data and put it back into the List (kinda a modify option). I have a different ComboBox with where I select an ID which matches a memberID which in its turn retrieves the data from the List. I use the following code for that:
private void ModifyComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int modifyID = int.Parse(modifyComboBox.SelectedItem.ToString());
var player = players.Where(p => p.memberID == modifyID);
teamComboBox.SelectedValue = player.Select(p => p.team).FirstOrDefault(); // this isnt working as intended
firstNameTextBox.Text = player.Select(p => p.firstName).FirstOrDefault();
lastNameTextBox.Text = player.Select(p => p.lastName).FirstOrDefault();
ageTextBox.Text = player.Select(p => p.age.ToString()).FirstOrDefault();
salaryTextBox.Text = player.Select(p => p.salary.ToString()).FirstOrDefault();
yearsActiveTextBox.Text = player.Select(p => p.yearsActive.ToString()).FirstOrDefault();
minutesPerGameTextBox.Text = player.Select(p => p.minutesPerGame.ToString()).FirstOrDefault();
}
While the TextBoxes get filled with the correct data the ComboBox just gets blank.
This is de XAML code for the ComboBox if needed:
<ComboBox x:Name="teamComboBox" HorizontalAlignment="Left" Margin="200,38,0,0" VerticalAlignment="Top" Width="220">
<ComboBoxItem IsSelected="True">-Choose a team-</ComboBoxItem>
<ComboBoxItem>Minnesota Timberwolves</ComboBoxItem>
<ComboBoxItem>Charlotte Hornets</ComboBoxItem>
<ComboBoxItem>LA Lakers</ComboBoxItem>
</ComboBox>
In this case, the type of SelectedValue needs is ComboBoxItem instead of the string type you passed. If you want to set the SelectedValue programmatically, you can set the SelectedValuePath of teamComboBox as "Content" first, it means to set/get the Content of the ComboBoxItem(ie the string you set for the ComboBoxItem). Then you can set your p.team to SelectedValue. For example:
.xaml:
<ComboBox x:Name="teamComboBox" HorizontalAlignment="Left" SelectedValuePath="Content" Margin="200,38,0,0" VerticalAlignment="Top" Width="220">
<ComboBoxItem IsSelected="True">-Choose a team-</ComboBoxItem>
<ComboBoxItem>Minnesota Timberwolves</ComboBoxItem>
<ComboBoxItem>Charlotte Hornets</ComboBoxItem>
<ComboBoxItem>LA Lakers</ComboBoxItem>
</ComboBox>
I have the following code:
SubjectsToChooseFrom = new ObservableCollection<SubjectDTO>(_model.Subjects);
SubjectsToChooseFrom = SubjectsToChooseFrom.Where(x => x.Id == (int)CurrentProgId);
SubjectsToChooseFrom is bound to a ComboBox. After this, only one element gets shown in the box instead of a list. Why is this, and how can I make sure all the values where x.Id == (int)CurrentProgId stay in SubjectsToChooseFrom?
UPDATE
XAML and code as requested:
<ComboBox Text="Choose program" Margin="5" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding SubjectsToChooseFrom}"
SelectedValue="{Binding CurrentSubjectId, Mode=TwoWay}"
SelectedValuePath="Id"
DisplayMemberPath="SubjectName">
</ComboBox>
Declaration:
private IEnumerable<SubjectDTO> _subjectsToChooseFrom;
public IEnumerable<SubjectDTO> SubjectsToChooseFrom
{
get
{
return _subjectsToChooseFrom;
}
set
{
_subjectsToChooseFrom = value;
OnPropertyChanged();
}
}
Where returns an IQueryable. You need to use ToList() or ToArray() etc. to materialize the results of this query.
If you're using MVVM, much better is to expose both your collections as public properties and raise PropertyChanged whenever these collections change for any reason. You can then bind your ComboBoxes to these properties and they'll automatically get updated.
I ended up doing this by hand:
await _model.LoadAsync();
SubjectsToChooseFrom = new ObservableCollection<SubjectDTO>(_model.Subjects);
ObservableCollection<SubjectDTO> temp = new ObservableCollection<SubjectDTO>();
foreach (var subject in SubjectsToChooseFrom) {
if(subject.ProgId == CurrentProgId)
{
temp.Add(subject);
}
}
SubjectsToChooseFrom = temp;
I am creating a small wpf programme which filters a dataset by date. It will eventually group and sum the data, but at the moment I am just trying to display the data rows. I want a window with a ComboBoxat the top with possible dates to select and a DataGridshowing the correct records. I have bound the ComboBoxto a list of possible dates, and I have bound a label to the value that the ComboBoxupdates. This label updates when I select a value from the ComboBox. I cannot however get the DataGrid to update to show the new data.
A DataSet is passed to the ViewModel constructor which extracts two DataTables. One is used to create an ObservableCollection<DateTime> for the ComboBoxs items to bind to (through WorkItemsDates). The other is stored for the DataGrid to bind to via a filtering (and eventually grouping/summing etc.) property WorkItems. The ComboBox is bound to the DateTime SelectedDate. The label is also bound to SelectedDate to ensure it is being updated by the ComboBox
The Xaml View is as follows:
<Window ...>
<DockPanel>
<ComboBox DockPanel.Dock="Top"
ItemsSource="{Binding WorkItemsDates}"
SelectedItem="{Binding SelectedDate, Mode=TwoWay}"
ItemStringFormat="ddd d MMM yyyy"
IsSynchronizedWithCurrentItem="True" />
<Label DockPanel.Dock="Bottom"
Content="{Binding SelectedDate, FallbackValue=99/99/9999}"
ContentStringFormat="dd MM yyyy" />
<DataGrid Name="TimeTotalsDataGrid" AutoGenerateColumns="True"
ItemsSource="{Binding WorkItems, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True" ></DataGrid>
</DockPanel>
</Window>
And code behind:
public partial class TheView: Window
{
public UserControl1(DataSet tigerDataSet)
{
InitializeComponent();
DataContext = new TimeTotalsDateSelectorViewModel(tigerDataSet);
}
}
The ViewModel is as follows:
internal class TimeTotalsDateSelectorViewModel
{
private ObservableCollection<DateTime> _workItemsDates;
private DataTable _workItems;
private DateTime _selectedDate;
public TimeTotalsDateSelectorViewModel(DataSet tigerDataSet)
{
if (tigerDataSet == null)
throw new ArgumentNullException("workItemsDates");
if (tigerDataSet.Tables["WorkItemsDates"] == null)
throw new ArgumentNullException("tigerDataSet.Tables[WorkItemsDates]");
if (tigerDataSet.Tables["WorkItems"] == null)
throw new ArgumentNullException("tigerDataSet.Tables[WorkItems]");
_workItems = tigerDataSet.Tables["WorkItems"];
_workItemsDates = new ObservableCollection<DateTime>();
foreach (DataRow row in tigerDataSet.Tables["WorkItemsDates"].Rows)
{
_workItemsDates.Add((DateTime)row["FinishDate"]);
}
SelectedDate = _workItemsDates[0];
}
public ObservableCollection<DateTime> WorkItemsDates
{
get { return _workItemsDates; }
}
public DateTime SelectedDate
{
get { return _selectedDate; }
set { _selectedDate = value; }
}
public DataTable WorkItems
{
get
{
DataRow[] _workItemsToShow = _workItems.Select("FinishTime>='" + _selectedDate.ToString() + "' AND FinishTime<'" + _selectedDate.AddDays(1).ToString() + "'");
return _workItemsToShow.Count() != 0 ? _workItemsToShow.CopyToDataTable() : null;
}
}
First thing, as you are using MVVM, so you will have to notify your properties by implementing INotifyPropertyChanged interface.
Second from the Setter of your SelectedItem property you will have to notify your WorkItems property as well, so that when you change the date from dropdown, it will also update the ItemsSource of the DataGrid.
In the setter of SelectedDate, call the code that gets the values for the grid. You should be implementing INotifyPropertyChanged as well, so the UI will update the data and the user can see it.
I load a lists of objects in a datagrid with this:
dataGrid1.Items.Add(model);
The model become data from a database. It has a Id(int), Name(string) and Text(string)
In my datagrid I show only the Name of the model. How can I filter the datagrid now, when I enter something in a textbox?
I was at this page: http://msdn.microsoft.com/en-us/library/vstudio/ff407126(v=vs.100).aspx but I don't understand the code from there and I can not explain how I should transpose that for my problem.
there are multiple way's to filter Collection
let's suggesting this is your Item Class
public class Model
{
public string Name
{
get;
set;
}
}
and your collection looks like
var ObColl = new ObservableCollection<Model>();
ObColl.Add(new Model() { Name = "John" });
ObColl.Add(new Model() { Name = "Karl" });
ObColl.Add(new Model() { Name = "Max" });
ObColl.Add(new Model() { Name = "Mary" });
Way 1 (Predicate):
public MainWindow()
{
InitializeComponent();
// Collection which will take your ObservableCollection
var _itemSourceList = new CollectionViewSource() { Source = ObColl };
// ICollectionView the View/UI part
ICollectionView Itemlist = _itemSourceList.View;
// your Filter
var yourCostumFilter= new Predicate<object>(item => ((Model)item).Name.Contains("Max"));
//now we add our Filter
Itemlist.Filter = yourCostumFilter;
dataGrid1.ItemsSource = Itemlist;
}
Way 2 (FilterEventHandler):
public MainWindow()
{
InitializeComponent();
// Collection which will take your Filter
var _itemSourceList = new CollectionViewSource() { Source = ObColl };
//now we add our Filter
_itemSourceList.Filter += new FilterEventHandler(yourFilter);
// ICollectionView the View/UI part
ICollectionView Itemlist = _itemSourceList.View;
dataGrid1.ItemsSource = Itemlist;
}
private void yourFilter(object sender, FilterEventArgs e)
{
var obj = e.Item as Model;
if (obj != null)
{
if (obj.Name.Contains("Max"))
e.Accepted = true;
else
e.Accepted = false;
}
}
extended Information to Way 1
if need multiple conditions or some complex Filter you can add a method to your Predicat
// your Filter
var yourComplexFilter= new Predicate<object>(ComplexFilter);
private bool ComplexFilter(object obj)
{
//your logic
}
This is a simple implementation of using the Filter property of ICollectionView. Suppose your XAML contains this:
<TextBox x:Name="SearchTextBox" />
<Button x:Name="SearchButton"
Content="Search"
Click="SearchButton_OnClick"
Grid.Row="1" />
<DataGrid x:Name="MyDataGrid"
Grid.Row="2">
<DataGrid.Columns>
<DataGridTextColumn Header="Lorem ipsum column"
Binding="{Binding}" />
</DataGrid.Columns>
</DataGrid>
Then in the constructor you can get the default view for your data where you can set the filter predicate which will be executed for every item of your collection. The CollectionView won't know when it should update the collection, so you have to call Refresh when the user clicks the search button.
private ICollectionView defaultView;
public MainWindow()
{
InitializeComponent();
string[] items = new string[]
{
"Asdf",
"qwer",
"sdfg",
"wert",
};
this.defaultView = CollectionViewSource.GetDefaultView(items);
this.defaultView.Filter =
w => ((string)w).Contains(SearchTextBox.Text);
MyDataGrid.ItemsSource = this.defaultView;
}
private void SearchButton_OnClick(object sender, RoutedEventArgs e)
{
this.defaultView.Refresh();
}
At this url you can find a more detailed description of CollectionViews:
http://wpftutorial.net/DataViews.html
#WiiMaxx, can't comment as not enough rep. I would be a bit more careful about the direct casts there. They can be slow for one thing and for another, if the same filter was applied to a grid holding different complex type data you would have an InvalidCastException.
// your Filter
var yourCostumFilter= new Predicate<object>(item =>
{
item = item as Model;
return item == null || item.Name.Contains("Max");
});
This will not break you datagrid and will not filter the results if the cast fails. Less impact to your users if you get the code wrong. On top of that the filter will be faster due to the "as" operator not doing any explicit type coercion as the direct cast operation will.
You can use dataview filter in order to filter the datagrid rows.
DataView dv = datatable.DefaultView;
StringBuilder sb = new StringBuilder();
foreach (DataColumn column in dv.Table.Columns)
{
sb.AppendFormat("[{0}] Like '%{1}%' OR ", column.ColumnName, "FilterString");
}
sb.Remove(sb.Length - 3, 3);
dv.RowFilter = sb.ToString();
dgvReports.ItemsSource = dv;
dgvReports.Items.Refresh();
Where the "datatable" is datasource given to your datagrid and using string builder you build the filter query where "Filter String" is the text you want to search in your datagrid and set it to dataview and finally set the dataview as itemsource to your datagrid and refresh it.
take at look at DataBinding --> in your case dont add items to your grid, but set the itemssource
<Datagrid ItemsSource="{Binding MyCollectionOfModels}" />
or
dataGrid1.ItemsSource = this._myCollectionOfModels;
and if you want some kind of filtering,sorting, grouping look at CollectionView
I found a dumb method and know this is an old question but ... Just use the Filter function on items property on the DataGrid object. Like this: (I'm sorry but i learned only VB)
Public Property SearchName As String
Get
Return _SearchName
End Get
Set
_SearchName = Value
DG_drw_overview.Items.Filter = New Predicate(Of Object)(Function(x) x.Name.Contains(Value))
End Set
End Property
This property is changed every time you type something in the textbox. DG_drw_overview is the DataGrid instance. In Predicate the object represents the object you put in the DataGrid.
Then bind the SearchName to textbox
<TextBox x:Name="TB_search"
Text="{Binding SearchName, UpdateSourceTrigger=PropertyChanged}"/>
Set datacontext of the textbox to the main class (usually after InitializeComponent())
TB_search.DataContext = Me
Leaving this here incase like me you couldn't get the other methods above to work properly.
//Example of my Transaction class
//Transaction(ID, Amount, Description)
ListCollectionView collectionView = new ListCollectionView(List<Transaction>);
collectionView.Filter = (e) =>
{
Transaction transaction = e as Transaction;
if (transaction.Amount >= 0) //When this is true it returns all positive transactions
{
return true;
}
else
{
return false;
}
};
dataGrid.ItemsSource = collectionView;
I used this enclosed in if statements to make a filter by combobox for the datagrid.
The combobox has 3 options Credit/Debit/Credit & Debit, depending on the option I chose it would filter on a selection change event
I got this from here: http://dotnetpattern.com/wpf-datagrid-filtering
I'm using Entity Framework as my database source and need to convert a Linq query "var" type to an ObservableCollection. I then need to bind the ObservableCollection to a ComboBox on WPF form; binding to ItemsSource, DisplayMemeberPath, SelectedValuePath and SelectedValue.
Here is Code:
using (PulseContext pc = new PulseContext())
{
var maritalcodes = from m in pc.CodeMaster
where m.Type == "16"
select new { m.Code, m.Description };
prop.ClientData.Options = new ObservableCollection<object>(maritalcodes);
}
Problem is the ComboBox is showing this as "{ Code = ????, Description = ???? }" instead of bind to code for value and description for display. What do I have to do to get the ComboBox to bind to the individual elements?
You need to set SelectedValuePath and DisplayMemberPath like this:
prop.ClientData.Options = new ObservableCollection<object>(maritalcodes);
prop.ClientData.Options.SelectedValuePath = "Code";
prop.ClientData.Options.DisplayMemberPath = "Description";
Or you can set them in xaml like this:
<ComboBox ItemsSource="{Binding Path=maritalcodes}"
SelectedValuePath="Code"
DisplayMemberPath="Description" />
<ComboBox ItemsSource="{Binding Path=maritalcodes}"
SelectedValuePath="Code"
DisplayMemberPath="Description"
SelectedValue="{Binding Path=Code}"/>
I hope this will help.