|
I am creating a WPF ComboBox for a WPF Windows application dynamically. However, when I build a list of a class object with 2 items in the list and bind to the combobox, I see records in the combobox but do not see any text displayed. I am setting the DisplayMemberPath appropriately I think. Any ideas?
Class AnswerChoice
Public AnswerChoiceID As Integer
Public AnswerChoice As String
End Class
Private Function GetAnswerChoices() As List(Of AnswerChoice)
Dim lstAnswerChoices As New List(Of AnswerChoice)
Dim objAnswerChoice As AnswerChoice
objAnswerChoice = New AnswerChoice
objAnswerChoice.AnswerChoiceID = 1
objAnswerChoice.AnswerChoice = "Answer Choice One"
lstAnswerChoices.Add(objAnswerChoice)
objAnswerChoice = New AnswerChoice
objAnswerChoice.AnswerChoiceID = 2
objAnswerChoice.AnswerChoice = "Answer Choice Two"
lstAnswerChoices.Add(objAnswerChoice)
Return lstAnswerChoices
End Function
Dim ctrlComboBox As New ComboBox()
ctrlComboBox.Width = 300
ctrlComboBox.HorizontalAlignment = Windows.HorizontalAlignment.Left
ctrlComboBox.Name = "cboBox"
ctrlComboBox.ItemsSource = GetAnswerChoices()
ctrlComboBox.DisplayMemberPath = "AnswerChoice"
ctrlComboBox.SelectedValuePath = "AnswerChoiceID"
|
|
|
|
|
You need to make your fields in Class AnswerChoice Properties like this
Dim intAnswerChoiceID As Integer
Public Property AnswerChoiceID As Integer
Get
Return intAnswerChoiceID
End Get
Set(ByVal value As Integer)
intAnswerChoiceID = value
End Set
End Property
Dim strAnswerChoice As String
Public Property AnswerChoice As String
Get
Return strAnswerChoice
End Get
Set(ByVal value As String)
strAnswerChoice = value
End Set
End Property
My advice also would be to use an ObservableCollection rather than a List to store your collection, as it notifies your UI if there are any changes to the collection.
Hope this helps
When I was a coder, we worked on algorithms. Today, we memorize APIs for countless libraries — those libraries have the algorithms - Eric Allman
|
|
|
|
|
|
I used this same technique with a Tree View control it works really well.
|
|
|
|
|
It's always much better to work with Properties rather than public fields, as that way the field can only be updated internally in the class declaring them.
When I was a coder, we worked on algorithms. Today, we memorize APIs for countless libraries — those libraries have the algorithms - Eric Allman
|
|
|
|
|
I have a datagrid with checkboxes to select a level of permissions for a user and a text field to display the current level. I have subscribed to the SelectedItemChanged event of the datagrid to do the logic required to update my text field. But the property bound to the checkbox just checked has not been set true when my handler is called. Is there some other event I should use or something I can do so that the change is applied to the bound property when I evaluate things?
I have the binding mode set to TwoWay and the UpdateTrigger to PropertyChanged in the xaml.
Thanks,
Preston
|
|
|
|
|
Could you post the XAML for the DataGrid, and also let us know whether your classes implement INotifyPropertyChanged .?
When I was a coder, we worked on algorithms. Today, we memorize APIs for countless libraries — those libraries have the algorithms - Eric Allman
|
|
|
|
|
The class the collection is based on does implement INotifyPropertyChanged. Here's the grid XAML.
<DataGrid ItemsSource="{Binding CurrentBoxEmployeePermissions}"
x:Name="BoxesPageEmployeesGrid"
AutoGenerateColumns="False" SelectionMode="Extended"
Style="{DynamicResource MyDataGridStyle}"
ColumnHeaderStyle="{StaticResource GridHeaderStyle}"
IsSynchronizedWithCurrentItem="True"
ColumnWidth="SizeToCells"
CanUserResizeRows="False"
SelectionChanged="OnReevaluateAppliedBoxParameters">
<DataGrid.Columns>
<DataGridTextColumn x:Name="ColumnEmployeeName"
Header="Employee Name"
Binding="{Binding EmployeeName}"
Width="Auto"
MinWidth="180"
IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.FontSize"
Value="16" />
<Setter Property="TextBlock.FontWeight"
Value="Bold" />
<Setter Property="TextBlock.TextAlignment"
Value="Center" />
<Setter Property="TextBlock.VerticalAlignment"
Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<ATC_Admin:AutoCommitCheckBoxColumn Header="System User"
ElementStyle="{StaticResource CheckBoxStyle}"
EditingElementStyle="{StaticResource CheckBoxStyle}"
Width="Auto"
Binding="{Binding SystemUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ATC_Admin:AutoCommitCheckBoxColumn>
<ATC_Admin:AutoCommitCheckBoxColumn Header="Maintenance"
ElementStyle="{StaticResource CheckBoxStyle}"
EditingElementStyle="{StaticResource CheckBoxStyle}"
Width="Auto"
Binding="{Binding Maintenance, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ATC_Admin:AutoCommitCheckBoxColumn>
<ATC_Admin:AutoCommitCheckBoxColumn Header="Administrator"
ElementStyle="{StaticResource CheckBoxStyle}"
EditingElementStyle="{StaticResource CheckBoxStyle}"
Width="Auto"
Binding="{Binding Administrator, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ATC_Admin:AutoCommitCheckBoxColumn>
<DataGridTextColumn Header="Applied Permission"
Binding="{Binding AppliedPermission}"
Width="Auto"
MinWidth="180"
IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.FontSize"
Value="16" />
<Setter Property="TextBlock.FontWeight"
Value="Bold" />
<Setter Property="TextBlock.TextAlignment"
Value="Center" />
<Setter Property="TextBlock.VerticalAlignment"
Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
|
|
|
|
|
The collection the datagrid is bound to is an observable collection of this object.
public class BoxEmployeePermissions : INotifyPropertyChanged
{
private AdminMediator mMediator;
public BoxEmployeePermissions(AdminMediator mediator)
{
mMediator = mediator;
}
/// <summary>
/// Name of the Employee.
/// </summary>
public string EmployeeName
{
get { return mEmployeeName; }
set
{
mEmployeeName = value;
OnPropertyChanged("EmployeeName");
}
}
private string mEmployeeName;
/// <summary>
/// Employee's unique ID.
/// </summary>
public int EmployeeId
{
get { return mEmployeeId; }
set
{
mEmployeeId = value;
OnPropertyChanged("EmployeeId");
}
}
private int mEmployeeId;
/// <summary>
/// Basic system user for this box.
/// </summary>
public bool SystemUser
{
get { return mSystemUser; }
set
{
mSystemUser = value;
if (value)
{
Maintenance = false;
Administrator = false;
}
OnPropertyChanged("SystemUser");
mMediator.BoxEventAggregator.GetEvent<ReevaluateAppliedBoxParameters>().Publish(new object());
}
}
private bool mSystemUser;
/// <summary>
/// Maintenance level access to the box.
/// </summary>
public bool Maintenance
{
get { return mMaintenance; }
set
{
mMaintenance = value;
if (value)
{
SystemUser = false;
Administrator = false;
}
OnPropertyChanged("Maintenance");
mMediator.BoxEventAggregator.GetEvent<ReevaluateAppliedBoxParameters>().Publish(new object());
}
}
private bool mMaintenance;
/// <summary>
/// Administrator level access to the box.
/// </summary>
public bool Administrator
{
get { return mAdministrator; }
set
{
mAdministrator = value;
if (value)
{
SystemUser = false;
Maintenance = false;
}
OnPropertyChanged("Administrator");
mMediator.BoxEventAggregator.GetEvent<ReevaluateAppliedBoxParameters>().Publish(new object());
}
}
private bool mAdministrator;
...
And things do update eventually. It just takes leaving the row with the checkbox in it and sometimes coming back to that row and leaving again before the update occurs.
|
|
|
|
|
I'm not sure how I would do that. I need to do a fair amount of evaluating things when one of those checkboxes is clicked. And I need the values of the properties to be up to date when I do that evaluation. Clearly the property attached to the checkbox has not been updated yet when the selectedItemChanged event is fired but I have yet to come up with an event I could use instead or a way to force the update of the property. I've been struggling with this for over a day now. Very frustrating for something that seems like it just oughta work.
|
|
|
|
|
IValueConverter and IMultiValueConverter are pretty convenient when it comes to value conversions in a software but, as we all know, there is never one way to do things.
The below scenario is very simple: a Model exposes properties to a ViewModel and these values need to be presented in the View.When these values need to be displayed in a specific way - say, the dimensions of an image diplayed with the appropriate units in a status bar - the above interfaces would do the job. However...
Some one said once that a ViewModel is simply "a converter in steroids" and he seems to have a point. The ViewModel could do the conversions via some simple routines and expose the results to the view for binding. This way we avoid loading our application with classes that - most probably - will be used only once. On the other hand, we bulk the ViewModel with lines and lines of code, loosing the focus and reducing maintainability.
What do you think? Which, in your opinion, is the best practice? I would really like to hear your opinions on the matter!
|
|
|
|
|
If you are very concerned about performance(or lots of data needs to be loaded) in the view, you can do the conversion in the view model.
If your view does not require too much data to be loaded, a converter can be used. It decouples the UI from the view model even further making the entire decoupling neater.
Just my view!
|
|
|
|
|
In MVVM, IValueConverter and IMultiValueConverter are very rarely used because the VM does that work.
|
|
|
|
|
This is exactly what I'm talking about! But...is it really preferable to end up with a hefty ViewModel as opposed to a few more classes?
|
|
|
|
|
If you are finding that your VM is doing too much repackaging, its likely a deficiency in your model . One thing that I like to do is make the model a partial class, so it can be easily extended in the UI without value converters or adding a ton of repacking in the VM. You'll find that a lot of stuff from Microsoft does exactly this. ADO.NET and WCF all make the models partial.
|
|
|
|
|
It really depends what you're trying to do. If it's purely acting on the VM to do something visual, I'd use a converter. If it was a deriving a new value for display from a current one, I'd rather do it in the VM. So, what do I mean by doing something visual? Well, in one application we wrote, we had to format the display to act like a typical accounts system and display negative numbers in red. In this case, adding the red brush in the VM was not one we felt comfortable with; using an instance of IValueConverter was a much better choice for us.
|
|
|
|
|
As is the case with the MVVM pattern in general, there is no clear black/white answer to this. As there isn't in the case of Validation: ValidationRule or IDataErrorInfo? Maybe that gray nature of the pattern - depends on what YOU want to do - is the strength and, in the same time, its weakness. And that confusion is what prevents me, so far, to take a deep dive in it.
|
|
|
|
|
MVVM is the Peter Parker of patterns. With great power comes great responsibility.
|
|
|
|
|
A lot of times, partial classes work great. Lets you keep the data provider layer from having to know about GUI specific crap and it rids you of value converters and data repackaging.
|
|
|
|
|
WCF and ADO.NET (and most ORMs and code generators) use partial classes. They are very useful if used properly.
Imagine your data provider has a class called SomeEntity:
class SomeEntity
{
string Name;
string Address;
}
Now what if you want to display certain names in a different color? You could add a value converter, but its much slicker and cleaner to change the class to:
partial class SomeEntity
{
string Name;
string Address;
}
and then in your GUI have:
partial class SomeEntity
{
Color txtColor;
}
and all you really need to do is properly fill in txtColor. No need for a converter .
-- modified 10-Oct-11 17:45pm.
|
|
|
|
|
It was only a quick example of partial classes, not production or working code. You kind of missed my point(s) though:
1) code generators such as ado.net, wcf, etc. *already give* you partial classes, might as well take advantage.
2) partial classes are not in the VM, they are in the partial class.
3) often times, you need to pass in multiple objects to value converters so you have all the info you need... heck, I've written a value converter when I needed to pass in 7 objects. SEVEN!
4) the VM doesn't set the properties, the object does.
Here, since you didn't like my previous example, I'll give you a better one. Lets say you wanted the TxtColor to be black unless the persons age was < 21 where it would be red. Ok, so a value converter would pretty much just need the age, not a big deal. But the partial class would just expose an additional TxtColor property. It would also watch for changes to the 'Age' property so it could update itself.
Lets say that to decide on the TxtColor you needed additional input... the age, maybe the value of a radio button, the value of a check box, the type of user, etc. Now you are starting to grab the data from so many places that you need to pass a lot into the value converter. Since that happens via data binding, you add an additional expense as well. Could be useful if some of those values are going to change of course , but if its just a static property, a value converter is not really the best solution.
|
|
|
|
|
I'm working on a custom startpage. I created a user control project and have the user control working in the start page. Now I want to add a command to it, like it shows here
http://msdn.microsoft.com/en-us/library/ee649080.aspx[^]
When I add the references and namespaces to my user control project that it says to add (aliased as SP and VS), and compile I get the error:
The type or namespace name 'VisualStudio' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)
The startpage project has the exact same references and that project compiles.
Anyone have any idea wy this isn't working?
Thanks
Everything makes sense in someone's mind
|
|
|
|
|
A little code might help but I would suggest ensuring that your namespaces are the same. Seems you are trying to reference a namespace named 'Microsoft'.
|
|
|
|
|
That's exactly what I'm trying to do. But the compiler doesn't like it.
Here's the XAML from the start page template:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sp="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.StartPage"
xmlns:vs="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.10.0"
xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.10.0"
mc:Ignorable="d"
xmlns:my="clr-namespace:ProjectTree.Views;assembly=ProjectTree"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="600" d:DesignWidth="800">
Here's the XAML in my project:
<UserControl x:Class="ProjectTree.Views.ProjectsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sp="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.StartPage"
xmlns:vs="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.10.0" xmlns:local="clr-namespace:ProjectTree.Models"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
d:DesignHeight="250"
d:DesignWidth="250">
I refererenced the same assemblies, and copied the namespace XAML from the other project. So it should compile.
Everything makes sense in someone's mind
|
|
|
|
|
I figured it out.
For some crazy ass reason my project was set to compile to 4.0 Client Profile.
Everything makes sense in someone's mind
|
|
|
|