I've a table in a database where I need to show all the rows's content (as a TextBlock) and to the right of each TextBlock I need to show a TextBox so the user can enter a value (a number) for each row and also I need to be able to change the color of any TextBox when the value provided by the user is negative.
Can someone give me a clue with this?
PD: I'm using WPF with Prism 4 and MVVM pattern and VS2010 Ultimate
I won't give you a complete solution, but I can point you in the right direction.
I start by creating a data structure that contains properties for Name and Value, and that implements INotifyPropertyChanged for Property Change notification.
Next in the ViewModel (or possibly Model), I would make would be an ObservableCollection<MyDataObject>, and populate it with the data from the database.
In the XAML, I would use an ItemsControl bound to the collection, and overwrite the ItemTemplate to render each item as either a Horizontal StackPanel or a Grid, containing the Label and TextBox
For the TextBox.Foreground property, I would bind it to the same value that TextBox.Text is bound to, except I'd also use a IValueConverter in the binding which checks to see if the value is above or below 0, and returns the correct color. Since it is a binding, it will automatically update whenever the value changes.
<TextBox Text="{Binding Value}"
Foreground="{Binding Value, Converter={StaticResource MyCustomConverter}}" />
It'd be nice to know what you've tried so we can help you better, but you almost certainly want to be binding your data to a DataGrid or, if you absolutely need more flexibility (so far it doesn't sound like that's the case), an ItemsControl. You don't want to be just creating a Grid dynamically.
Your question about the TextBox and how to change it's color when the value is negative is actually a separate question from how to do your layout. I'd look into the Validation components of WPF for that.
Related
I have a WPF application to display incoming bytes from a serial stream. I want to display these bytes inside a user control that allows for changes (i.e. byte value changes meaning background color changes to alert user, user chooses to view data as hex/decimal/binary). So far, I have an
ObservableCollection<ByteDisplay>
where ByteDisplay is a WPF User Control bound to a data model with a few relevant properties: DisplayFormat (enum Hex/Binary/Decimal), Data (the actual byte value), and BgColor (a string representing a system color to denote that a value has changed).
My problem is that I have to completely replace the DataContext of the ByteDisplay to get changes to show in the aggregate view that holds this collection. I think I could get around this with a ValueConverter for the collection of raw bytes, instead of completing the change in the Aggregate View's ViewModel, but is this the right approach?
Why do you want to use an ObservableCollection? Sounds really strange though.
In my opinion, you can simply create a user control bound to a view model by following the MVVM pattern.
Here you can have textblocks, textboxes, datagrids, charts, etc which binds to properties in the view model. You could also bind the background property to viewmodel or use a converter as you said. Like if you have some other property referring to that, say you're displaying some value in a textbox and you use the same value for changing the background color to warn the user, then use a converter.
It's as simple as this. I did not understand the logic behind creating an ObservableCollection still.
I'm in the process of learning WPF and currently exploring data binding. I have a DataGrid control on my form, and in my C# code for the form I have a List<string> variable.
I want to be able to use the Properties UI for the DataGrid in the designer to bind the List<string> to the DataGrid. I cannot figure out what I need to do or where I need to look in the UI to do this.
This is what I am doing:
Click my DataGrid in the UI designer to make it the active control.
Move to the Properties window.
Scroll down to the ItemsSource property.
Click on the field and the UI with Source, Path, Converter and Options pops up.
And when I get to this point I no longer know what to do.
I do not want to accomplish this by writing/modifying XAML. I want to know how it works using the UI.
Having never used the designer before, I can't be totally sure (your use case isn't quite clear either).
That being said, in my designer you
Set the "Binding Type" to "Data Context"
Select the "Custom" text box (needed for me because it doesn't see my DataContext)
Type the name of your property in the "Path" field (you can only bind to Properties)
Hit OK.
Note that this is the same as writing in XAML:
<DataGrid ItemsSource="{Binding MyItemCollection}"/>
<!-- or --!>
<DataGrid ItemsSource="{Binding Path=MytItemsCollection}"/>
There's a reason no one uses the designer....
The other options are more "advanced" binding concepts that you don't normally use on ItemsSource properties.
Note that DataGrid is a poor choice for displaying strings. ListView or ListBox are much better choices, as they don't assume your information has multiple pieces (like DataGrid does).
I understand not liking XAML, as it really intimidated me at first, but I will quickly say that it is a powerful tool. I am not sure how to do it through the designer, but in C# let's say you name your DataGrid 'myDataGrid' and your List is named 'stringList'. It is as simple as the following:
myDataGrid.ItemsSource = stringList;
and the data grid is now bound to your string list.
Thanks for asking the question! The properties window is so underrated.
First you must set the DataContext.
It's in the common section of the properties window. Set the data context to whatever view model you need. If you don't have a VM and the List is in the code behind, set the data context to relative source self.
Next in the Path write the name of your List.
Also, you may want to use ObservableCollection instead of List so your objects are updated in the UI as they change.
I have a program in MVVM. In this program i have a view with a DataGrid. In this datagrid i have to show something like this:
The user mark some cells and after MouseUp, should be created a button over this selected cells. The first Problem here is, how to get the Column and the Row. It is in the SelectedCellsChanged-Event Argument. With help from the WPF MVVM Light i can give my ViewModel the Event and the Arg, but my workmate had already problems with visual elements in a ViewModel(He was get a error in the visual tree). And i know that a visual element should not be in a VM. Can anyone here give me a way to get this Column and Row-Spawn?
My second problem that i can't imagine me how to create the buttons in the correct cell. If i think right is it possible to build a new VM for every Button(with Property StartRowSpawn, EndRowSpawn, StartColumnSpawn, EndColumnSpawn) or for each cell?
Can you give me tip, how to solve that?
I thinked about a grid too. Get position while MouseDown and MouseUp and then find the Grid.Column and Grid.Row for that.
Edit
Okay i done my first problem with Interfaces, but i already have my second problem. Can anyone help me there?
I would rethink why you need to do this(or maybe you can explain more why to need buttons like this). What I do when I need to change the design of data in a data grid is use an alternate datatemplate or the details datatemplate for the datagrid row to chnage the format of the infomration, then you can use the same view model. You can bind the column spans to properties in your viewmodel but it gets difficult if you want the button quantities to be dynamic.
I have a datagrid that is bound to a collection of my ViewModel. One of its column has values that are very specific to business requirements. On this column it can contain an alphanumeric characters.
For example I can have column values A1,A20,AA,AA12,AAA. Now I want to custom sort this values, say I want the anything with most letters should go first or etchetera. There is a default sorting with DataGrid but only do normal sorting.
My question is how would you implement this through MVVM? We can get away with this through subscribing to an event in code behind and re arrange the whole collection. However this is not what I want, I am looking for suggestions or solutions on how to approach this.
I found this link Sorting on datagrid column with binded data and converter that is attaching a property for a DataGrid but what I want to do is to attach a property to be updated every time a user click on this column. Is it possible to attach a property in a DataGrid Column?
Possible duplicate of : Sorting on datagrid column with binded data and converter but this is not using MVVM.
There are several strategies, but the most immediately accessible is to set up a DataGrid something like this...
<DataGrid ItemsSource="{Binding DriveList}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" SortMemberPath="DriveType"/>
</DataGrid.Columns>
</DataGrid>
This example shows the grid binding to a list of drives on the host machine. The first column shows the information is bound to the property 'Name'. BUT when you click the column header, it will sort on a property which is not displayed, 'DriveType'. Strange example, but it works fine.
So in your app, you would amend your collection items to include a property that is not displayed, and populate it with values according to what you want. In the example in your question, you might use something like...
MySortString = MyName.ToString().Length;
And that will cause the sort to do what you are looking for, i.e., the longest value of 'MyName' will be first with shorter values after that. You have to repopulate 'MySortString' property every time you change sort methods or reload your data source.
This strategy is MVVM compliant because all you are doing in the VM is populating an additional property. Plus you can unit test it immediatly with Nunit or whatever.
I created an attached behavior which does this in a nice MVVM-friendly way:
WPF DataGrid CustomSort for each Column
Hopefully this addresses your problem.
I have a ComboBox whose ItemSource is bound to a list of strings (idealy i would use an Enum), this is done using the MVVM pattern.
Now i want to bind an object to the ComboBox, it's called SelectedUser and i want to bind its property: UserType, which is a string.
So i have got this:
<ComboBox ItemsSource="{Binding Path=Usertypes}" SelectedValue="{Binding Mode=TwoWay, Path=SelectedUser.UserType}" />
It works and it does change the value of the selected user if i play with it, but the problem is, that it does not display anything in the ComboBox unless i select a user, and then change the ComboBox selection, then it works, but only for that user.
I tried playing around with DisplayMemberPath, SelectedValuePath and SelectedItem,
when i added those the ComboBox did not show anything in it (there were still options to select from, but they were invisible or something).
So what should i do? Is this a bug?
I have to mention that i have got another ComboBox that has a list of ints, and it works fine.
Update:
I was informed that I'm getting this issue because the string I'm comparing to the string in the comboBox, are not actually the same.
My string comes from the Entity Framework via Ria Services. (User.UserType)
And when it compares it to the list of strings in the ComboBox ItemSource, they are not equal, for some strange reason.
And i also heard, i might have to override Equal method for that check.
but I'm not sure where and how to do so.
Is the view notified if the SelectedUser changes? I could imagine that this might be the problem; if there is no such notification the ComboBox will not reload the SelectedValue if another user is selected, it will only update the binding if you make changes yourself.
If that is not it, you also need to consider that no selected value will be displayed unless the current value exactly matches one of values in the source list.