Changing the path a property is bound to at runtime - wpf

I have a ComboBox with a list of objects bound to it.
Currently i have the items templated so they show only the property Class.Name. so the ComboBox is full of Class.Name
However i am required to give the user the option to display the property Class.Description instead. If it were just that easy i would be fine, but they want the option to switch back and forth between them at runtime.
Any ideas?

You can probably do this directly in WPF.
I would alter the business objects to include an additional Readonly property, something like, DisplayTextProperty
Public ReadOnly Property DisplayTextProperty()
Get
If ShowDescription Then
Return Description
Else
Return Name
End If
End Get
End Property
I have done this in a few places now and it works great.

Related

WPF ViewModel and View

I've been programming in WPF for a while now coming from Windows Forms. Because of this I've always been linking my View and ViewModel directly together by giving my controls in the View a name and use that name in the ViewModel to update my View.
Now I did some searching and found out that there is something called MVVM. I've been studying it for a week now and tried to make my application 'better'.
First, I started using binding with Dependency Properties like this:
Public Shared SelKlantProperty As DependencyProperty = DependencyProperty.Register("SelKlant", GetType(String), GetType(MainWindow))
Public Property SelKlant As String
Get
Return DirectCast(GetValue(SelKlantProperty), String)
End Get
Set(value As String)
SetValue(SelKlantProperty, value)
End Set
End Property
I now have about 50 Properties made like that and my code is getting big pretty fast of those. Also my ClassDiagram is filled with the Fields of the Public Shared DependencyProperty and Properties of the Public Property.
I'm wondering, is there an easier or much better way to bind View and ViewModel? Most of the Properties I use are to put Text in TextBoxes.
You don't need to create dependency properties for your view model properties - regular properties will suffice. You will however have to use dependency properties if you're going to bind to that property (for example, TextBox.Text is a dependency property - you wouldn't be able to bind to it if it wasn't). You can keep your properties as they were - just make sure you implement INotifyPropertyChanged.
Dependency properties have other uses other than just for data binding, but I won't elaborate as it's a very large subject and you can find in other resources. Other than that, you can read more on how to implement the MVVM pattern here. This page also includes examples where you can see that the viewmodel does not need to have any dependency properties defined.

Problem with filtering DataGrid

I have bound my DataGrid to a DataTable and only few of the details are displayed in the grid. When I wanted to filter the DataGrid I created a View with my DataGrid's ItemsSource.
Code:
Dim myView As ICollectionView = CollectionViewSource.GetDefaultView(MyDGrid.ItemsSource)
myView.Filter = New Predicate(Of Object)(AddressOf filterFunc1)
Now When I do the search, the non-displayed fields are also included in the search.
Public Function filterFunc1(ByVal obj As Object) As Boolean
Dim filStr As String = "*" & TextBox1.Text & "*"
For Each item As String In obj.Row.ItemArray
**If item.ToLower Like filStr.ToLower Then**
Return True
End If
Next
Return False
End Function
Also I Have ComboBox fields in the DataGrid which are loaded separately from other DataTable's. Now I cant Include them in the search.
A screenshot from my App:
So how do I make a search that includes only the Text from Displayed part.
EDIT: Also how do I skip searching the null valued fileds? 'cause thats causing an exception in my case.
Well then...
Your question is pretty disjointed and I can't understand all of it - maybe that's why you didn't get an answer so far. Skipping null fields is simply a matter of adding a new condition in filterFunc1 - if Convert.IsDBNull(item) then continue for (assuming item is a field in a DataRow, of course).
However, this programming style is pretty foggy and I'd recommend at the very least being more clear on which columns you filter, and the types of objects in the columns. A much better approach would be to map the data you're getting from the database to actual objects in your application - that allows for more type-safe programming. I think the main problem here is that nobody can really tell what's going on there from a few lines of code because nobody can make any assumptions about what kind of objects are there.
About the items in the ComboBox, no idea what kind of difficulties you're having, you might want to clear that up a bit.
you could maintain, instead of simply strings, structures containing both captions and IDs, like say
public class YourComboItem
public property Id as string [get/set]
public property Title as string [get/set]
end class
Then bind your ComboBox's ItemsSource to a collection of these items retrieved from the database, and set DisplayMemberPath to Title and ValueMemberPath to Id. Then you can use the ComboBox's SelectedValue to get the selected ID. As you can see, having objects instead of raw data structures can have quite some advantages.
Of course, I described getting the SelectedValue directly from the ComboBox, while a much better architecture would be MVVM, with the ViewModel containing an ObservableCollection(Of YourComboItem) and the ComboBox's ItemSource bound to it with an actual binding. Then you can also bind the SelectedItem to a property in your ViewModel, and have the item as a whole, including both Id and Title, to work with without knowing anything about your user interface. Alternatively you could have an ICollectionView generated from the collection of items and bind the ItemsSource to that, then you'd have the selected item in the ICollectionView's CurrentItem property.
I'd really recommend reading up on MVVM (Model-View-ViewModel) to make your work with WPF a whole lot easier.

DataContext, DataBinding and Element Binding in Silverlight

I'm having one hell of a time trying to get my databinding to work correctly. I have reason to believe that what I'm trying to accomplish can't be done, but we'll see what answers I get.
I've got a UserControl. This UserControl contains nothing more than a button. Now within the code behind, I've got a property name IsBookmarked. When IsBookmarked is set, code is run that animates the look of the button. The idea is that you click the button and it visually changes. We'll call this UserControl a Bookmark control.
Now I have another control, which we'll call the FormControl. My FormControl contains a child Bookmark control. I've tried to do databinding on my Bookmark control, but it's not working. Here's some code to help you out.
This is the XAML and Loaded event handler of my control. As you can see it contains a child element that is a custom control (bookmark). So once this control loads, it's DataContext is set to an new instance of an Employee object. Silverlight also sets the DataContext property of my child bookmark control to the same instance. I've verified this by debugging. If my parent has a valid DataContext set then why can't my child control (bookmark) property databind to it?
<UserControl ......>
<q:Bookmark x:Name="BookMarkControl1" IsBookmarked="{Binding IsSiteBookmarked}" />
</UserControl>
public void Control_Loaded(object sender, EventArgs e)
{
DataContext = new Employee { IsSiteBookmarked = True };
}
This is my custom control below. Obviously it contains more than this, but for readability I've trimmed it down to the property I'm trying to databind to.
//this is the bookmark control. I've included this control within another control, and I'm trying to databind to properties within my parents DataContext
public partial class Bookmark : UserControl
{
bool _IsBookmarked= false;
public bool IsBookmarked
{
get {return _IsBookmarked;}
set {
_IsBookmarked= value;
SwitchMode(value);
}
}
}
UPDATE
Got some javascript errors that I should mention. Firebug reports a AG_E_PARSER_BAD_PROPERTY_VALUE exception. It doesn't seem like my databinding is even working yet.
Make your IsBookmarked property on the Bookmark control a dependency property.
I presume Control_Loaded is a part of your FormControl, in which case I'm not sure you are using DataContext properly. Best double check that.
UPDATE: Yes, you are using the DataContext properly. AG_E_PARSER_BAD_PROPERTY_VALUE indicates you need to make the IsBookmarked property a dependency property, like so:
Public Property IsBookmarked() As Boolean
Get
Return Me.GetValue(IsBookmarkedProperty)
End Get
Set(ByVal value As Boolean)
Me.SetValue(IsBookmarkedProperty, value)
End Set
End Property
Public Shared ReadOnly IsBookmarkedProperty As DependencyProperty = DependencyProperty.Register("IsBookmarked", GetType(Boolean), GetType(Bookmark), New PropertyMetadata(New PropertyChangedCallback(AddressOf OnIsBookmarkedPropertyChanged)))
Private Shared Sub OnIsBookmarkedPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim cntrl As Bookmark = TryCast(d, Bookmark)
cntrl.SetIsBookmarked(e.NewValue)
End Sub
If you only need to store the value for later use, then you don't need to do anything in the OnIsBookmarkedPropertyChanged procedure, But I put some code there as an example anyway.
Good Luck!
I don't recall the exact order in which databinding is evaluated (and I'm too lazy to go look it up), but as I recall, it initially happens BEFORE the form's Loaded event fires, and without making the IsBookmarked property a dependency property, or at least using INotifyPropertyChanged, it may have trouble establishing the datacontext appropriately. I'd recommend either implementing INotifyPropertyChanged or making IsBookmarked a dependency property. DataBinding is tough enough to get right in the best of circumstances (see my long, bad-tempered rant about it here), and you'll just be making it more difficult on yourself if you aren't setting up your properties in the way that it expects.
The control exposes a IsSiteBookmarked property(which I believe should be a DependencyProperty) but the control is binding to a IsBookmarked which is not shown. Is this intentional? Have you checked your Visual Studio output window for binding errors?
Addition 1:
Since you have fixed the typo in your question and added that there is an error being reported.
Start by clearing up the AG_E_PARSER_BAD_PROPERTY_VALUE problem. Is there a line number and start position in the error message? Start looking there. One strategy is to start taking out XAML until there is no longer an error. This will narrow down the offending code.
Running in debug, mode check for binding errors in the output window.
You might want to also post the Employee class code, especially the IsSiteBookmarked property.
Typically when doing databinding to an object you will want to leverage the INotifyPropertyChanged interface and implement that so that the control can properly invalidate it's property value. Unless you use INotifyPropertyChanged with Mode=TwoWay then any code that changes your DataContext's IsSiteBookmarked will have no effect.

Programatically setting design-time properties in Windows.Forms control

Is there an easy way to programatically setting a property value on a control such that it will be persisted in the designer-generated code?
I imagine a piece of code in the control constructor or load event which is executed when i open the control in design mode, but sets a property such that it will be persisted the same way as if I changed the value manually through the properties grid.
Edit: Yes, this would be the same as editing the designer code manually, but I want to do it programatically.
Presuming I understand the question
You can databind that property to a setting, using the Visual studio Gui. Check the properties for that control, under the Data section for (Application Settings), (Property Bindings).
It depends on what kind of functionality you want. If you only need the properties to be set when you add the control to a form, then setting the properties in the control's constructor works perfectly. However, changes you make using the Properties panel will take precedence, and setting properties in the control's constructor won't necessarily affect existing instances of the control.
If you want to be able to change the properties for instances of the control in one place, assigning bindings in (application settings), (property bindings) works. Then you can modify all the bindings from the Settings.settings file. This still requires you to assign property bindings for each instance of the control, though.
Now for the finale. If you want to set properties in the control's class that affect all instances of the control, whether the instances are yet to be created or already exist, you have to get a little creative. I found a solution, but it may not be the best. My solution goes like this:
In the control's constructor, for each property you want to set, you:
Store the desired value in a private variable.
Assign the variable's value to the property.
Assign an event handler that assigns the variable's value to the property whenever the property is changed.
A downside is the amount of coding for each property. Also, you wouldn't be able to change the properties from the Properties pane.
Do you think about something like:
if (this.DesignMode)
{
// do somthing
}
If you put this into the constructor remember to call InitializeComponent() before.
What about:
Private Function GetPropertyByName(ByVal propName As String) _
As PropertyDescriptor
Dim prop As PropertyDescriptor
prop = TypeDescriptor.GetProperties(l_dWindow)(propName)
If prop Is Nothing Then
Throw New ArgumentException( _
"Matching ColorLabel property not found!", propName)
Else
Return prop
End If
End Function
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
GetPropertyByName("AnyPublicProperty").SetValue(AnyControl, "AnyStringVALUE")
Me.DialogResult = DialogResult.OK
End Sub

WPF Bind to DependencyProperty of another Object?

I am working on a WPF application similar to visio. I would like to be able to logically group some of the items in my diagram, each of which is a UIElement, and control certain behaviors (i.e. visibility) at the group level.
My first attempt at this was to create a control, called a Group, which had width & height = 0. I wanted to assign to my diagram elements a specific "Group" through their group property, and then bind certain UIElement properties to the group value, as below:
<DiagramNode
Width="300" Height="300"
Visibility="{Binding RelativeSource={RelativeSource Self},Path=Group.Visibility}"
> ... </DiagramNode >
Although this does not throw a binding error, it also doesn't work. Changing the Visibility of the group has no affect on the visibility of the nodes assigned to that group. No errors appear at anytime as far as i can tell, it just doesn't work.
Any ideas? Is my approach possible? If no, any one have alternatives they'd like to suggest :). I'm not a huge UI guy, feel much more comfortable in a service layer, so I'm open to other suggestions.
If there really is no binding error in the trace of the application when run through the debugger, then the problem is probably in change notifications. You must make sure that the Visibility property of your Group object provides change notifications when changed.
This is usually done by implementing INotifyPropertyChanged on the class, and in the set accessor raising a PropertyChanged event (if the value actually changed).
Is the issue perhaps in my property declaration of the Group object of my DiagramNode class?
Public Class DiagramNode
...
Private _group As Group
Public Property Group() As Group
Get
Return Me._group
End Get
Set(ByVal value As Group)
Me._group = value
End Set
End Property
...
End Class

Resources