WPF binding to a boolean on a control - wpf

I'm wondering if someone has a simple succinct solution to binding to a dependency property that needs to be the converse of the property. Here's an example
I have a textbox that is disabled based on a property in the datacontext e.g.:
<TextBox IsEnabled={Binding CanEdit} Text={Binding MyText}/>
The requirement changes and I want to make it ReadOnly instead of disabled, so without changing my ViewModel I could do this:
In the UserControl resources:
<UserControl.Resources>
<m:NotConverter x:Key="NotConverter"/>
</UserControl.Resources>
And then change the TextBox to:
<TextBox IsReadOnly={Binding CanEdit,Converter={StaticResource NotConverter}} Text={Binding MyText}/>
Which I personally think is EXTREMELY verbose
I would love to be able to just do this(notice the !):
<TextBox IsReadOnly={Binding !CanEdit} Text={Binding MyText}/>
But alas, that is not an option that I know of.
I can think of two options.
Create an attached property IsNotReadOnly to FrameworkElement(?) and bind to that property
If I change my ViewModel then I could add a property CanEdit and another CannotEdit which I would be kind of embarrassed of because I believe it adds an irrelevant property to a class, which I don't think is a good practice.
The main reason for the question is that in my project the above isn't just for one control, so trying to keep my project as DRY as possible and readable I am throwing this out to anyone feeling my pain and has come up with a solution :)

Related

Binding three levels down in the data hierarchy

It's a good thing I don't mind feeling stupid.
I'm trying to bind to an ObservableCollection on my view model. The data hierarchy looks like: Parent -contains list of- Child objects. Nothing complicated.
At the outermost grid of my Xaml tree I establish a link to the view model with:
<Grid DataContext="{StaticResource src}">
Yes, src does reference the view model and the two dozen bindings before the problem textbox work fine. There is not another DataContext in my Xaml tree. Now I come to a simple textbox. I want to bind Textbox text to a child.property.
This works:
<TextBlock
DataContext="{Binding Parent}"
Text="{Binding Path=Child.Property}"
Style="{StaticResource headerMajor}"
/>
This doesn't work:
<TextBlock
Text="{Binding Source=Parent,Path=Child.Property}"
Style="{StaticResource headerMajor}"
/>
I thought they were two ways of saying the same thing. Ordinarily I wonder for a moment and then keep on coding. However, some advice I've read mentioned that DataContext attributes buried in Xaml controls can lead to hard to find bugs.
Please explain why one works and the other does not. This will help my grasp on the whole binding topic.
Jim
Source is a property which holds an object used as source for the binding, it does not resolve to a property. Hence your binding is looking for the property path Child.Property on the string "Parent", see the problem?

SelectedItem of SelectedItem

first of all I would like to thank you for the many good posts that i read in this forum. Unluckily I could not find anything of help for my current problem (either here or anywhere else).
What I'm trying to do sounds quite simple, but I have no clue how to get it to work ... perhaps I'm still to new to wpf or I don't thing wpfy enough :)
I'm designing a front end for a part in an automated manufacturing:
I have a quantity of places where pallets can be put (but it can be empty as well).
Each pallet has up to 3 places where parts can be mounted
Everything is created dynamically of a database and is reacting to changes.
The position of the parts on the pallet comes from the database as well and should be visualized
What I would like to have is:
An overview over the pallet-places with a preview of the pallet
When I select a place I want to see a detail view of the place
When I click on a part on the pallet of the detailed pallet I want to see details to the part
The first two points are quite simple and work nicely:
I have got a DataTemplate for every component (part, pallet, pallet-place). Actually those are UserControls that are imported as Datatemplates
the overview is a ListBox with the places as DataContext
for the detail-place-view I use the UserControl and bound it to the SelectedItem of the Listbox
I tried to bind the Text of a Textblock to the ID of the selected Part ... and fail.
Probably I could use some global variables in the code behind - but that sound very ugly.
Can anybody help?
I have got a solution ... it is not nice but works.
I created an event in the pallet, that triggers, when the selected part-place changes
I handle the event in the pallet-place and create a new one
And finally I handle it in the overview and change the detailview accordingly
Most likely there are much nicer solutions, but it will suffice.
Perhaps try an ElementName binding?
<TextBlock Text="{Binding ElementName=Name_of_your_Listbox, Path=SelectedItem.ID" />
Can you post a bit more code of your TextBlock and your Binding?
Context is important, if i use a ContentControl and bind its content to the SelectedItem like this:
<ContentControl Content="{Binding SelectedItem, ElementName=mylistbox}">
I can bind to the ID of the selected item in the DataTemplate like this:
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ContentControl.ContentTemplate>
That is because setting the Content of the ContentControl automatically sets the DataContext as well, and this binding is relative to the DataContext since no source (ElementName, RelativeSource, Source) has been specified.
I do not know how your UserControl handles the context, if the DataContext is not affected such bindings will not work. You would need to bind directly then:
<uc:MyDetailsView Data="{Binding SelectedItem, ElementName=mylistbox}">
<!-- ... -->
<TextBlock Text="{Binding SelectedItem.ID, ElementName=mylistbox}" />
This of course defeats the purpose of having the binding on the UserControl itself in the first place. But unless you post some relevant code it's quite hard to tell what is wrong.
Also check the Output window in VisualStudio, binding errors will show up there and might provide valuable information as to what went wrong.

XAML DataContext and ViewModel Type

I use MVVM for my application, the DataContext of controls is assigned in my c# code (not in XAML).
Therefore the XAML controls have no idea to which instance type its DataContext is set to. The consequence is that there is no refactoring support and intellisense for the bound properties of my viewmodel in XAML.
Is there a way to tell a control in XAML to which type its DataContext is linked?
So when I modify a property name in my ViewModel or search for all references of that property, I want that this property in XAML bindings gets considered too.
There is no framework support, the best you can do is tell the VS designer the 'shape' of the DataContext so that it will give you hints for the properties. If you want to make your solution more refactor-proof, I would recommend Daniel's T4 metadata solution:
http://www.codeproject.com/KB/codegen/T4Metadata.aspx
This generatesmetadata for your view models which you can reference in the XAML:
<StackPanel DataContext="{Binding Source={StaticResource Person}}">
<TextBlock >Name:</TextBlock>
<TextBox Text="{Binding Path={x:Static Metadata:PersonMetadata.NamePath}}"/>
</StackPanel>
Colin E.
No, as the DataContext can change at runtime, it doesn't make sense to tie this to a particular type.

How to set ItemsSource?

This dialog makes no sense to me
http://img576.imageshack.us/img576/4223/50709706.gif
And I'm having trouble finding good tutorials on it. Most of the examples aren't detailed enough, or do stuff via code, but I'd like to take advantage of the IDE as much as possible.
Whats the difference between ItemsSource and DataContext?
I'd like to bind it to just a List for starters. I don't need SQL or databases or anything fancy. Where would I declare my list? In MainWindow.xaml.cs? How do I get it to appear in that dialog?
Think of "DataContext" as the default value for "Source" in a binding.
When you create a binding, you can specify the path and source, like this (I'll use TextBox as an example):
<TextBox Text="{Binding Path=Foo,Source={StaticResource Bar}}" />
So my TextBox.Text property is bound to a Foo property on an object called Bar (a resource somewhere in the application).
However, if you have a whole bunch of things that you want to bind to properties on Bar, it's easier to set Bar as the DataContext of the parent container. A Binding without a Source will just use the DataContext by default, and DataContext flows through to child controls from the parent. So:
<StackPanel DataContext="{StaticResource Bar}">
<TextBox Text="{Binding Path=Foo}" />
<TextBox Text="{Binding Path=Fizz}" />
<TextBox Text="{Binding Path=Buzz}" />
</StackPanel>
All of the TextBoxes are still binding to properties on Bar, but they're doing it without setting it as a Source explicitly.
So let's have another look at the dialog you posted. It's giving you several options for the "source" of the ItemsSource binding. When you choose "DataContext", you're telling Visual Studio that the ItemsControl doesn't need to know the source - it'll pick it up from the DataContext of the parent container (maybe even the Window itself).
If you chose one of the other options (ElementName, RelativeSource or StaticResource) then you'd be setting the binding's source explicitly for that ItemsControl.
Once you've told it that it's binding to the DataContext, you'll need to drop into the "Path" section of the dialog and tell it which property to bind the items of the control to. In the end, the markup would look something like this (assuming it's a ListBox):
<ListBox ItemsSource="{Binding Path=Foos}" />
So the items in the ListBox are coming from a property called "Foos", and that property is on an object that's set in the DataContext somewhere higher in the logical tree (perhaps on the Window itself).
You rarely need to use the data context of a control outside of the control. The most common use case for setting DataContext(DataContext = this;) is within UserControl's code-behind to make all controls within the UserControl to bind to the control's properties.
When you use a ListBox, setting ItemsSource is sufficient, unless you are doing something funky.
This is a pretty good walkthrough: http://windowsclient.net/learn/video.aspx?v=315275
Specifically, you need to set the DataContext first to tell it where to look for the ItemsSource. The easiest way is to set this on the Window through the XAML:
<Window.DataContext>
<controllers:DownloadManager />
</Window.DataContext>

WPF: Can I bind to a method of the selected object in another control?

I have two WPF list boxes. One is a list of lists (actually a List of ObservableCollection), the other is a list of all known instances of "Thingy".
Here's the datatemplate I'm using for the "thingy" class.
<DataTemplate DataType="{x:Type Model:Thingy}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="ThingyInListCheckBox" Click="ThingyInList_Click"></CheckBox>
<TextBlock Text="{Binding Path=ThingyName}"></TextBlock>
</StackPanel>
Here's the XAML for the list boxes:
<ListBox
Name="ListOfGroups"
SelectionMode="Single">
</ListBox>
<ListBox
Name="ListOfThingys"
SelectionMode="Single">
</ListBox>
I have the data binding for the list boxes set up in code, because I'm too tired to figure out how to do it in XAML:
ListOfGroups.ItemsSource = InMemoryCache.ThingyGroups;
ListOfThingys.ItemsSource = InMemoryCache.Thingys;
What I want is the checkbox "ThingyInListCheckBox" to be checked if the 'thingy' object is in the list that is the selected item in the "ListOfGroups" listbox. So basically I need to bind it to the "Contains" method of the "ListOfGroups".SelectedItem while passing it the "ListOfThingys".SelectedItem as a parameter.
I'm tempted to do this all in code, but I'm trying to get a better understanding of XAML data binding because I hate myself and I want me to suffer.
Is this even possible, or have I hit the inevitable "wall of databinding" that exists in every other data binding system in the history of software development?
It is possible, in fact the hard thing is that there are many ways to do this and you have to choose one. None of them is a simple addition to your current code. However there is one way, by which you gain more than solving your problem. Actually, it is more of a pattern, called MVVM (some might argue about the naming).
Here is a small explanation on your example.
Suppose ThingyGroup has an IsSelected property, which is bound to the IsSelected property of the containing ListBoxItem. Again, suppose Thingy has a Group property too. Then you can use Group.IsSelected as a path to bind checkbox. Notice that there is still a small issue that IsSelected is a bool and IsChecked is a nullable bool.
A search on MVVM should give you concrete samples.

Resources