I have a combobox in my windows application which is DataBound to a readonly list.My requirement is to show some of the items in Bold based on a property of the list. The property is different from that of value member and Display Member. Is there anyway to do it without looping through each of the item, as the list is too big?
Key off of the selected item.
public Form1()
{
_dataItems = new List<DataItem>
{
new DataItem {Name = "Alpha", IsBold = true, OtherData = new object()},
new DataItem {Name = "Beta", IsBold = false, OtherData = new object()},
new DataItem {Name = "Gamma", IsBold = true, OtherData = new object()},
};
this.InitializeComponent();
comboBox1.DrawItem += comboBox1_DrawItem;
comboBox1.DataSource = _dataItems;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "OtherData";
}
void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
var dataItem = (DataItem)comboBox1.Items[e.Index];
if (dataItem.IsBold)
e.Graphics.DrawString(dataItem.Name, BoldFont, SystemBrushes.ControlText,
e.Bounds);
else
e.Graphics.DrawString(dataItem.Name, NormalFont, SystemBrushes.ControlText,
e.Bounds);
}
The DataItem class:
public class DataItem
{
public String Name { get; set; }
public bool IsBold { get; set; }
public Object OtherData { get; set; }
public override string ToString()
{
return Name;
}
}
Related
i want to show some values in a data Grid and these values are not from database.
I am making a POS in which when user enters an item it should be shown to him/her in a data grid form.
This is what i've tried and my mistake was i didn't bind the "Name","Price" but now i've correct it and now it works perfectly
public struct MyData
{
public int Price { set; get; }
public string Name { set; get; }
}
public MainWindow()
{
InitializeComponent();
DataGridTextColumn grid_C1 = new DataGridTextColumn();
DataGridTextColumn grid_C2 = new DataGridTextColumn();
dGrid.Columns.Add(grid_C1);
dGrid.Columns.Add(grid_C2);
grid_C1.Binding = new Binding("Name");
grid_C2.Binding = new Binding("Price");
grid_C1.Header = "Name";
grid_C2.Header = "Price";
dGrid.Items.Add(new MyData { Name = "dumyText", Price = 2 });
dGrid.Items.Add(new MyData { Name = "dumyText", Price = 2 });
}
I'm working on a Winforms ReactiveUI app and I have a UserControl that implements IViewFor:
public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{
public CustomViewModel ViewModel { get; set; }
object IViewFor.ViewModel {
get { return ViewModel; }
set { ViewModel = value as CustomViewModel; }
}
public CustomView()
{
InitializeComponent();
this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);
}
}
In the calling control, I set the ViewModel with:
customView.ViewModel = new CustomViewModel(model)
However, when data changes, customView.ViewModel is re-assigned (using the same code above) but it does not automatically re-bind. I'm assuming that's because ViewModel has no PropertyChanged event.
I could implement INotifyPropertyChanged on CustomView, but I was wondering - is there a convenience method/ReactiveUI way of doing this?
I think you are on the right track with passing a new model instead of replacing the ViewModel. I wasn't sure of your exact requirements, but here is an example that might help. Selecting a new user changes the list of contacts in the CustomView's DataGridView.
Item
public class Item
{
public string Name { get; set; }
public int Value { get; set; }
public Item(string name, int value)
{
Name = name; Value = value;
}
}
MainViewModel
public class MainViewModel : ReactiveObject
{
int _userId;
public int UserId
{
get { return _userId; }
set { this.RaiseAndSetIfChanged(ref _userId, value); }
}
ObservableAsPropertyHelper<User> _user;
public User User => _user.Value;
ObservableAsPropertyHelper<List<Item>> _userList;
public List<Item> UserList => _userList.Value;
public ReactiveCommand<User> LoadUser { get; protected set; }
public ReactiveCommand<List<Item>> LoadUserList { get; protected set; }
public MainViewModel()
{
LoadUser = ReactiveCommand.CreateAsyncObservable(_ => LoadUserImp(UserId));
_user = LoadUser.ToProperty(this, x => x.User, null);
LoadUserList = ReactiveCommand.CreateAsyncObservable(_ => LoadUserListImp());
_userList = LoadUserList.ToProperty(this, x => x.UserList, new List<Item>());
// Listens for change to UserId and loads new User.
this.WhenAnyValue(x => x.UserId).Where(id => id > 0).InvokeCommand(LoadUser);
}
private IObservable<User> LoadUserImp(int userId)
{
User user;
if (userId == 1)
{
user = new User { Id = 1, Name = "Bob" };
}
else
{
user = new User { Id = 2, Name = "Jane" };
}
return Observable.Return(user);
}
private IObservable<List<Item>> LoadUserListImp()
{
Item item1 = new Item("Bob", 1);
Item item2 = new Item("Jane", 2);
List<Item> items = new List<Item> { item1, item2 };
return Observable.Return(items);
}
}
MainView
public partial class MainView : Form, IViewFor<MainViewModel>
{
public MainViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = value as MainViewModel; }
}
public MainView()
{
InitializeComponent();
List<Item> items = new List<Item>();
UserComboBox.DataSource = items;
UserComboBox.DisplayMember = "Name";
UserComboBox.ValueMember = "Value";
// Two way binding.
this.Bind(ViewModel, vm => vm.UserId, v => v.UserComboBox.SelectedValue);
// One way binding.
this.OneWayBind(ViewModel, vm => vm.UserList, v => v.UserComboBox.DataSource);
// Per Paul Betts: Invoking this in the VM constructor means that your VM class becomes more difficult to test,
// because you always have to mock out the effects of calling [LoadUserList],
// even if the thing you are testing is unrelated.
// Instead, I always call these commands in the View.
this.WhenAnyValue(v => v.ViewModel.LoadUserList)
.SelectMany(x => x.ExecuteAsync())
.Subscribe();
// This is where I would change your model, in this case the user in the CustomView.
this.WhenAnyObservable(v => v.ViewModel.LoadUser)
.Subscribe(user => customView1.ViewModel.User = user);
ViewModel = new MainViewModel();
customView1.ViewModel = new CustomViewModel();
}
}
CustomViewModel
public class CustomViewModel : ReactiveObject
{
ObservableAsPropertyHelper<List<Person>> _contacts;
public List<Person> Contacts => _contacts.Value;
User _user;
public User User
{
get { return _user; }
set { this.RaiseAndSetIfChanged(ref _user, value); }
}
public ReactiveCommand<List<Person>> LoadContacts { get; protected set; }
public CustomViewModel()
{
LoadContacts = ReactiveCommand.CreateAsyncObservable(_ => LoadContactsImp(User.Id));
_contacts = LoadContacts.ToProperty(this, x => x.Contacts, new List<Person>());
this.WhenAnyValue(vm => vm.User.Id).InvokeCommand(LoadContacts);
}
private IObservable<List<Person>> LoadContactsImp(int userId)
{
List<Person> contacts;
if (userId == 1)
{
contacts = new List<Person>()
{
new Person() { Id = 1, FirstName = "John", LastName = "Jones" },
new Person() { Id = 2, FirstName = "Beth", LastName = "Johnson" },
};
}
else
{
contacts = new List<Person>()
{
new Person() { Id = 1, FirstName = "Dave", LastName = "Smith" },
new Person() { Id = 2, FirstName = "Elizabeth", LastName = "Bretfield" },
};
}
return Observable.Return(contacts);
}
}
CustomView
public partial class CustomView : UserControl, IViewFor<CustomViewModel>
{
public CustomViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = value as CustomViewModel; }
}
public CustomView()
{
InitializeComponent();
this.OneWayBind(ViewModel, vm => vm.Contacts, v => v.Contacts.DataSource);
}
}
Why the CustomView constructor is called "BridgeGeometryView"?
Are you using ReactiveList or ReactiveBindingList for SomeBindingList in the ViewModel?
Also, I recommend to not set the binding directly, use WhenActivated after InitializeComponent() method:
public BridgeGeometryView()
{
InitializeComponent();
this.WhenActivated(() =>
{
this.Bind(ViewModel, x => x.SomeBindingList, x => x.DataGridBindingSource.DataSource);
});
}
I know, there are a lot of examples but I'm not getting it to work.
I have a Silverlight Mask where I dynamically add UIElements like Textboxes or ComboBoxes. So far, this is working fine. Now I'm trying to set the ComboBox Selected Item from my database values.
I store the information for generation in my own classes.
public class Metadata
{
public int? MetadataId { get; set; }
public string Name { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Column { get; set; }
public MetaDataType MetaDataType { get; set; }
public List<MetadataData> MetadataData { get; set; }
}
public class MetadataData
{
public int MetadataDataId { get; set; }
public int MetadataId { get; set; }
public string Description { get; set; }
}
public partial class MetadataStore
{
public Guid MetadataStoreId { get; set; }
public Guid ObjectId { get; set; }
public Guid ModuleTypeId { get; set; }
public int MetadataSetId { get; set; }
public int? MetadataSetSetId { get; set; }
public int MetadataId { get; set; }
public string Description { get; set; }
public int? RowId { get; set; }
public DataContainer.Profile userProfile { get; set; }
}
My selected value stores the MetadataDataId as a String in my MetadataStore in the field Description. I'm generating the UIElements based on the MetaData class in code-behind like this
switch ((MetaDataDataType)metadata.MetaDataType.MetaDataTypeId)
{
case MetaDataDataType.String:
frmElement = new TextBox() { Name = String.Format("dynCtrl_{0}_{1}", metadata.MetadataId, metadata.Name)
AcceptsReturn = false, Margin = new Thickness(4),
HorizontalAlignment = HorizontalAlignment.Left,
Height = metadata.Height, Width = metadata.Width,
Style = App.Current.Resources["TextBoxStyleFlat"] as Style };
frmElement.SetBinding(TextBox.TextProperty,
new Binding { Mode = BindingMode.TwoWay, Path = new PropertyPath(metadata.Name) });
break;
case MetaDataDataType.SingleSelection:
frmElement = new ComboBox()
{
Name = String.Format("dynCtrl_{0}_{1}", metadata.MetadataId, metadata.Name),
Margin = new Thickness(4),
HorizontalAlignment = HorizontalAlignment.Left,
Style = App.Current.Resources["ComboBoxStyleFlat"] as Style,
ItemsSource = metadata.MetadataData,
DisplayMemberPath = "Description",
//SelectedItem = metadata.Name,
Height = metadata.Height,
Width = metadata.Width
};
frmElement.SetBinding(ComboBox.SelectedValueProperty, new Binding { Mode = BindingMode.TwoWay, Path = new PropertyPath(metadata.Name) });
frmElement.SetBinding(ComboBox.SelectedValuePathProperty, new Binding { Mode = BindingMode.TwoWay, Path = new PropertyPath("MetadataDataId") });
default:
break;
}
I thought I could make it work with an Dictionary<string, object>
var p = new Dictionary<string, object>();
p[mData.Metadata.Name] = Convert.ToInt32(item.Description);
but as it didn't work, I ended up with generating a DataTable and selecting the first element which gets me an Object with my desired dynamic properties.
TempObject.Testprop
TempObject.Nutzen
The DataSourceCreator is used from http://blog.bodurov.com/How-to-Bind-Silverlight-DataGrid-From-IEnumerable-of-IDictionary/.
internal static void generateDataContext(List<MetadataSetMetadata> metadataSetMetadata, List<MetadataStore> metadataStore, ref Grid viewGrid)
{
if (metadataStore != null && metadataStore.Count > 0)
{
dynamic expObj = new ExpandoObject();
var p = new Dictionary<string, object>();
foreach (var item in metadataStore.Where(x => x.MetadataSetSetId == null))
{
var mData = metadataSetMetadata.FirstOrDefault(x => x.Metadata != null && x.Metadata.MetadataId.Equals(item.MetadataId));
switch ((MetaDataDataType)mData.Metadata.MetaDataType.MetaDataTypeId)
{
case MetaDataDataType.String:
case MetaDataDataType.LongString:
p[mData.Metadata.Name] = item.Description;
break;
case MetaDataDataType.SingleSelection:
if (!String.IsNullOrWhiteSpace(item.Description))
{
p[mData.Metadata.Name] = Convert.ToInt32(item.Description);
}
break;
default:
break;
}
}
var emtyRowList = new List<IDictionary>();
emtyRowList.Add(p);
viewGrid.DataContext = DataSourceCreator.ToDataSource(emtyRowList).Cast<object>().FirstOrDefault();
viewGrid.UpdateLayout();
}
}
I set the DataContext for the complete StackPanel. My TextBox shows the correct Text stored in TempObject.Testprop. My ComboBox` only displays its Items Source but its not showing my already selected item, which Id is stored in TempObject.Nutzen.
Any ideas what I'm missing?
UPDATE:
I could make it working, binding the SelectedValue to the MetadataData Object instead of its ID value and setting the SelectedValuePath to the ID property name of MetadataData.
I could make it working, binding the SelectedValue to the MetadataData Object instead of its ID value and setting the SelectedValuePath to the ID property name of MetadataData.
This is my object structure
class object
{
string projectname;
string projectid;
list<string> associated_students;
}
//The List I am binding to the grid
list<objects> objectList = getList();
dataGridView.Source =objectList;
Now I want to bind the combo box inside the datagrid with the list "associated_students"
If I understand the question, you want each row to be tied to an object within your list of objects and you want the third column to show a combobox of that object's unique list of associated students. If I am correct, a simple search leads to this similar question:
How do I set up a DataGridView ComboBoxColumn with a different DataSource in each cell?
To solve, you need to manually bind each row. I was able to duplicate your problem and came up with this solution:
Your class "object"
public class Assignment
{
public Assignment()
{
this.Associated_Students = new List<string>();
}
public string ProjectName { get; set; }
public string ProjectID { get; set; }
public List<string> Associated_Students { get; set; }
}
And in Form1:
public Form1()
{
InitializeComponent();
this.Assignments = new List<Assignment>()
{
new Assignment()
{
ProjectID = "1",
ProjectName = "First",
Associated_Students = new List<string>() { "Me", "You", "Him", "Her" }
},
new Assignment()
{
ProjectID = "2",
ProjectName = "Second",
Associated_Students = new List<string>() { "Foo", "Bar" }
}
};
this.BindDGViewToList();
}
public List<Assignment> Assignments { get; set; }
public void BindDGViewToList()
{
DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();
col1.Name = "Project Name";
col1.ValueType = typeof(string);
dataGridView1.Columns.Add(col1);
DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
col2.Name = "Project ID";
col2.ValueType = typeof(string);
dataGridView1.Columns.Add(col2);
DataGridViewComboBoxColumn col3 = new DataGridViewComboBoxColumn();
col3.Name = "Associated Students";
col3.ValueType = typeof(string);
dataGridView1.Columns.Add(col3);
for (int i = 0; i < this.Assignments.Count; i++)
{
DataGridViewRow row = (DataGridViewRow)(dataGridView1.Rows[0].Clone());
DataGridViewTextBoxCell textCell = (DataGridViewTextBoxCell)(row.Cells[0]);
textCell.ValueType = typeof(string);
textCell.Value = this.Assignments[i].ProjectName;
textCell = (DataGridViewTextBoxCell)(row.Cells[1]);
textCell.ValueType = typeof(string);
textCell.Value = this.Assignments[i].ProjectID;
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)(row.Cells[2]);
comboCell.ValueType = typeof(string);
comboCell.DataSource = this.Assignments[i].Associated_Students;
dataGridView1.Rows.Add(row);
}
}
Note: This will display what you are asking for but you will have to handle updating your data. I would suggest researching BindingList over List objects. There may be better solutions, but this worked quickly for me.
I have the following property of type string.
[Category("General")]
[DisplayName("Book Name")]
public string BookName
{ //getter;
//setter;
}
When binding an object containing this property to propertygrid, I would like to provide a list of type string as source.
List<string> booksource = new List<string>();
When Property is of type enum, it automatically populates combobox, I want to acheive same functionality through collection.
Edit:
Expanded:
enum BookType
{
Novel = 0,
Magazine = 1
}
class Class1
{
string _bookname = "Book 1";
BookType _booktype = BookType.Magazine;
[Category("General")]
[DisplayName("Book Name")]
public string BookName
{
get { return this._bookname; }
set { this._bookname = value; }
}
[Category("General")]
[DisplayName("Book Type")]
public BookType BookType
{
get { return this._booktype; }
set { this._booktype = value; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Class1 obj = new Class1();
this.wpfpropertygrid.SelectedObject = obj;
}
}
For the above code, the propertygrid displays a combobox with items "Magazine" and "Novel" for property BookType and a textbox with text "Book 1" for property BookName. I want the property BookName displayed as combobox to which i can explicitly provide a source. I would like to bind a list {"Book 1","Book 2","Book 3"} to property BookName, so that the user can select any one of them.
Better late than never ;-)
With PropertyGrid from Extended WPF Toolkit you can do this that way:
enum BookType
{
Novel = 0,
Magazine = 1
}
public class BookItemsSource : IItemsSource
{
public ItemCollection GetValues()
{
var books = new ItemCollection();
books.Add("Book 1");
books.Add("Book 2");
books.Add("Book 3");
return books;
}
}
public class Class1
{
string _bookname = "Book 1";
BookType _booktype = BookType.Magazine;
[Category("General")]
[DisplayName("Book Name")]
[ItemsSource(typeof(BookItemsSource))]
public string BookName
{
get { return this._bookname; }
set { this._bookname = value; }
}
[Category("General")]
[DisplayName("Book Type")]
public BookType BookType
{
get { return this._booktype; }
set { this._booktype = value; }
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Class1 obj = new Class1();
this.wpfpropertygrid.SelectedObject = obj;
}
}