WPF Data Binding of Width Property - wpf

I'm trying to bind the Width property of a Canvas to the Width property of a Shape instance. So the Shape Width should be updated when the Canvas Width gets a new value
I want to do that in code, without XAML, because I create these elements on runtime.
I tried this, but it didnt work (the code is inside of my Canvas):
Binding binding = new Binding();
binding.Mode = BindingMode.OneTime;
binding.Source = this;
binding.Path = new PropertyPath("Width");
shape.SetBinding(FrameworkElement.WidthProperty, binding);
Thanks a lot for your help!
ksman

OneTime looks wrong. I think you want to use OneWay or TwoWay. Check the BindingModes
Edit
Since OneWay and ActualWidth didn't fix your problem, I should recommend you use a tool for debugging bindings. I use Snoop because it is free, but there are others. Debugging WPF without a tool like this can be painful.

You probably want to bind to ActualWidth and not Width.
Width is an input into the layout process -- it's something you may (but don't have to) specify.
ActualWidth is an output of the layout process -- it's the actual width that you see on the screen. This is partly determined by Width, if present, but other factors go into it too (especially if you didn't explicitly set Width in your XAML).

Related

WPF schema - what defines a ListBox to have a ScrollViewer?

For years, I have felt I don't have a good understanding of WPF because I haven't found an authoritative reference on the possibilities. For example, I just found out that a ListBox has an attached ScrollViewer property.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Auto">
Other than reading a book or article that tells that, how would I know that ScrollViewer is a valid attached property? Is there a XAML schema document or something? How does Visual Studio Designer know?
ScrollViewer isn't an attached property; it's a class.
ScrollViewer.HorizontalScrollBarVisibility is an attached property. But it's not an attached property that ListBox "has"; it's an attached property that can be attached to any DependencyObject at all, including ListBox.
Here's what you see if you right click on ScrollViewer.SetHorizontalScrollBarVisibility and ScrollViewer.GetHorizontalScrollBarVisibility. A pair of static methods like this is required for an attached property. The first parameter is the thing you're attaching the property to. It doesn't have to be DependencyObject; it could be FrameworkElement, ListBox, ItemsControl, or anything else that can support dependency properties.
// Summary:
// Sets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property to a given element.
//
// Parameters:
// element:
// The element on which to set the property value.
//
// horizontalScrollBarVisibility:
// The property value to set.
public static void SetHorizontalScrollBarVisibility(DependencyObject element, ScrollBarVisibility horizontalScrollBarVisibility);
//
// Summary:
// Gets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property from a given element.
//
// Parameters:
// element:
// The element from which the property value is read.
//
// Returns:
// The value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property.
public static ScrollBarVisibility GetHorizontalScrollBarVisibility(DependencyObject element);
The ListBox itself quite likely has no clue what ScrollViewer.HorizontalScrollBarVisibility means, or even that it exists. But in the ListBox's template, there's probably a ScrollViewer, which will probably have a binding a lot like this:
<ScrollViewer
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
Put that attached property on any control that might have a ScrollViewer in its template, and if the template was written correctly, the ScrollViewer will use that value.
This is really, really nice because ListBox doesn't have to think about its own scrolling behavior. That's all delegated. Building controls by composition is extremely powerful.
The downside is that the whole thing is just a vast box of undifferentiated barf and it's real hard to make sense out of stuff. Intellisense can't tell you much about the scrolling behavior of ListBox when nobody outside that particular ListBox's template at the moment can even guess what that scrolling behavior might be.
So, in answer to your question: Yes. Basically you just have to read a lot of stuff. And keep a "Cool XAML Tricks" text file to note down cool stuff you hear about that you don't have a use for just yet.
But that's the way this profession has always been. Even with Intellisense, you can't use a class you don't know about.
Today I learned about ColumnDefinition.SharedSizeGroup and Grid.IsSharedSizeScope, and I learned that you can derive value converters from MarkupExtension and give them strongly typed, named properties instead of having to pass some goofy string into CommandParameter.
I believe the best docs we have right now are the MSDN documentation pages for WPF controls. For example, if you look up ListBox, you can find information about the attached ScrollViewer, and also it's full default template.
https://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx

How to add borders to WPF ListView in code

I have created a ListView with a GridView in code.
ListView gridList = new ListView();
GridView gridListView = new GridView ();
gridList.View = gridListView;
Now, I define a GridViewColumn, set the header, width and bindingPath. All good and the data shows up.
GridViewColumn listColumn = new GridViewColumn();
listColumn.Header = "Some Header";
listColumn.Width = 100.0;
listColumn.DisplayMemeberBinding = new Binding("Name");
gridListView.Columns.Add(listColumn);
But there are no borders/gridlines shown on display of this ListView. How can I add borders through code?
Someone described my exact problem here but no good solution mentioned
http://social.msdn.microsoft.com/Forums/en-US/fa4fa8e0-81fe-487a-8763-590062d29c06/wpf-listview-gridview-row-border?forum=wpf
The logic in WPF programming is totally different from what you've done in winforms. Everything related to UI should always be set up using XAML (as much as possible). The WPF library itself has many parts desgined mainly for use in XAML although there is always an equivalent codebehind. However that's when using codebehind may be awkward and non-intuitive (as well as straight-forward).
I understand that you want something like the ListView Grid in Winforms. In WPF that can be achieved easily if you use XAML code. Even in code behind, you can always build a Style or Template from XAML string (with the help of XamlReader). This approach is good for complex scenario but in this case I have another approach (don't use the XAML parser at all). This trick does render the grid which is good enough (and at best it can do for the trade-off of simplicity):
//we need an instance of Style to set to ListView.ItemContainerStyle
var style = new Style(typeof(ListViewItem));
//set the bottom border thickness to 1
var setter = new Setter(Control.BorderThickness, new Thickness(0,0,0,1));
style.Setters.Add(setter);
//set the border brush
var borderBrush = new LinearGradientBrush { StartPoint = new Point(0,0),
EndPoint = new Point(1,0)};
var gradStop = new GradientStop(Colors.Transparent, 0.001);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Green, 0.001);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Green, 0.999);
borderBrush.GradientStops.Add(gradStop);
gradStop = new GradientStop(Colors.Transparent, 0.999);
borderBrush.GradientStops.Add(gradStop);
setter = new Setter(Control.BorderBrush, borderBrush);
style.Setters.Add(setter);
yourListView.ItemContainerStyle = style;
Note that the default inner Border of each ListViewItem has a hard-coded CornerRadius of about 2, so by setting just the bottom BorderBrush to a solid brush such as Brushes.Green will show a little upwards curly line at the 2 ends of the bottom border. You can try it yourself. If this result is acceptable, the code can be shorter and simpler (because you don't have to define the GradientBrush to cut-off the 2 curly ends) like this:
setter = new Setter(Control.BorderBrush, Brushes.Green);
style.Setters.Add(setter);
If the behavior is still not what you want. You should try the approach I mentioned about using XamlReader to parse a XAML string and get an instance of whatever you want in codebehind. (you can search it yourself, it's easy to have some result).
I suggest you see this link, it contains a dynamic GridView created in code-behind that can be useful for your specific case. For the code sample that you provided, you didn't add ShowGridLines property.

Updating WPF TextBox properties after setting Text

When setting the Text property of a WPF TextBox control, other properties that should also change (as a side effect) do not change. In particular, I would like to check the value of the ExtentWidth property after setting Text, but it does not change. I've tried calling UpdateLayout() to no avail. In Windows.Forms, I would call DoEvents().
OK, here's some code. I put this in the Window_Loaded() event handler. The problem is that textBox.ExtentWidth doesn't change when textBox.Text changes. That doesn't really surprise me. I figure I need to call something like textBox.UpdateLayout() to make it recalculate ExtentWidth, but that didn't help. ExtentWidth does vary depending on what I initialize textBox.Text to in the Window's constructor, but that doesn't help me. I need to set several different Text values and get the corresponding ExtentWidth for each.
string initText = textBox.Text; // "textBox"
double extentWidth = textBox.ExtentWidth; // 39.3
textBox.Text = "short text";
extentWidth = textBox.ExtentWidth; // 39.3
textBox.Text = "Long enough to make a difference, eh?";
extentWidth = textBox.ExtentWidth; // 39.3
I found a solution to the specific problem of getting TextBox.ExtentWidth to change after setting Text. Setting Text will raise the LayoutUpdated event, and you can get the new value of ExtentWidth in a handler for LayoutUpdated.
I used this fact to create a subclass of WPF TextBox that displays an ellipsis when the text is too long for the visible area. I wrote a CodeProject article about it here.

How can I animate a property dynamically in a Silverlight 4 UserControl?

I've run into a puzzling limitation in a Silverlight 4 UserControl.
What I'm trying to achieve is to have a panel, which slides out from a minimised state when a button is pressed, but the title bar of it should be draggable with which this maximised state can be resized.
What I've done for the sliding out is to animate the MaxHeight property of the parent Grid of this panel which works quite well even with no hardcoded Height for the panel, but I don't know how can I make this dynamic.
Trying to bind a variable from the code-behind to the 'To' parameter of the 'DoubleAnimation' didn't work, it just silently gets ignored.
As I'm creating UserControls to represent Views, the elements with x:Name properties won't get autogenerated.
I tried to work around this using the code below which mimics what happens in the autogenerated code (with the added bonus of only being done after the layout is actually loaded):
public DoubleAnimation PanelOpenMaxHeightDoubleAnimation;
private void LayoutRoot_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var LayoutRootreference = sender as Grid;
PanelOpenMaxHeightDoubleAnimation = ((DoubleAnimation)(LayoutRootreference.FindName("PanelOpenMaxHeightDoubleAnimation")));
PanelOpenMaxHeightDoubleAnimation.To = 383;
}
This however breaks when trying to set the value of To, as FindName returns null (I have x:Name manually set in XAML for this particular animation to "PanelOpenMaxHeightDoubleAnimation"). I have the sneaking suspicion FindName can't pick DoubleAnimations up from VisualStates, only actual layout children?
I did find the documentation about XAML Namescopes at http://msdn.microsoft.com/en-us/library/cc189026(v=VS.95).aspx#UserControls, but didn't really understand what my options are from this paragraph (other than being very limited):
For the case of a UserControl, there is no equivalent template part attribute convention for parts of the UserControl in the definition XAML, nor is there a template applied at all. Nevertheless, the namescopes between definition and usage remain disconnected, because the definition namescope is defined and then effectively sealed when you package your UserControl into an assembly for reuse. A best practice here is to define your UserControl such that any value that needs to be set to modify the definition XAML is also exposed as a public property of the UserControl.
What does it mean by the last sentence?
Wondering can I do next? Should I try to generate the entire state from code?
Well, managed to work it out so I'm sharing the solution.
Instead of trying to get a reference to the DoubleAnimation in Resources, I named the Grid in the layout I want to animate and get a reference to that using the code in the original question:
var SlidePanel = ((Grid)(LayoutRootreference.FindName("SlidePanel")));
This does return the element and using that it's possible to create a DoubleAnimation and a Storyboard from scratch purely in code. I just used this code example as a starting point: http://msdn.microsoft.com/en-us/library/cc189069(VS.95).aspx#procedural_code
Best part is, you can change the DoubleAnimation.To parameter even after setting everything up in the Storyboard, so now what I'm doing is just resetting that to my calculated value every time before calling Storyboard.Begin().
It's a bit fiddly to set all these up manually, but at least it works nicely once you do.

WPF Binding with a Border

I have a group of borders that make up a small map. Ideally I'd like to be able to bind the border's background property to a property in a custom list and when that property changes it changes the background.
The tricky thing is, I have to do this in code behind.
Use the FrameworkElement.SetBinding method:
myBorder.SetBinding(Border.BackgroundProperty, "CurrentBackground");
or, if you need sources and converters and things:
myBorder.SetBinding(Border.BackgroundProperty,
new Binding(somePath) {
Source = something,
Converter = new WonderConverter()
// etc.
});

Resources