Connector Lines with textbox, binding issue - wpf

I want to draw a line and when I double click it I want to put a text on it. I was thinking of putting it in a contentcontrol and drawing a line, put a collapsed textbox on top, detect a double click, show the textbox set it to a textblock, etc. The only problem I run into I don't know what to set the coordinates of the line since it is inside the contentcontrol, so an actual line gets drawn. i've been stuck for hours, any help would be appreciated.
Basically I need an object with a start and end point properties which has the shape of a line , with a content presenter. But I don't know how to go about doing this. Any pointers would be appreciated.

We do the same for labeling our connections. If you draw your connection via a path you can use
LineGeometry.GetPointAtFractionLength(0.5, out midPoint, out tangetMidPoint);
That way you would have the center position on your geometry. Now you could store this into a dependency property which you use to position the label. Of course this must be called everytime your shape/geometry changes its position or size.
a small example for a control combining this.
public class LabeledLine : ContentControl
{
public static readonly DependencyProperty LabelPosition ...
public static readonly DependencyProperty LineGeometry ...
// call me everytime the LineGeometry gets changed.
public void UpdatePath()
{
LineGeometry.GetPointAtFractionLength(0.5, out midPoint, out tangetMidPoint);
LabelPosition = midPoint;
}
}
Your ControlTemplate would look something like that
<ControlTemplate TargetType="{x:Type local:LabeledLine}">
<Canvas x:Name="canvas">
<Path Data="{TemplateBinding LineGeometry}"/>
<TextBox Canvas.Left="{TemplateBinding LabelPosition.X}" Canvas.Top="{TemplateBinding LabelPosition.Y}"/>
</Canvas>
<ControlTemplate/>
Now to add the ContentControl functionality you could add the ContentPresenter in place of the TextBox.
Basically I need an object with a start and end point properties which
has the shape of a line
For that just add 2 dp properties for your 2 positions. Make sure to add a dependency property changed handler to call the UpdatePath method.

Related

`Window` `Width` and `Top` have local values

My main window's Height, Left, Top, and Width are all bound to their respective viewmodel properties through a style. I can confirm that these four properties in the view model are only ever set to 1920, 1920, 118, 1080 respectively.
But when I launch the app, the Top and Width properties on the main window are set to something else (Width will be 1440 and Top will be a random number usually less than 300). What would cause this?
Here's what I see when I Snoop the app. Notice how Top and Width come from a Local Value Source:
Strangely, when I right-click on those properties in Snoop and tell it to "Clear/Reset", then those properties begin behaving. What is Snoop doing that fixes this?
Other facts:
The getters for the Top and Width viewmodel properties are only called once while the main window is being initialized. The stack trace runs through framework binding initialization code.
The setters for the Top and Width viewmodel properties are only called once from the viewmodel constructor as it sets those properties to 118 and 1080 respectively.
The bindings for these four properties are all two-way.
None of these things cause the view's properties to change/be correct:
Changing the associated viewmodel properties at runtime, even after the view has been fully loaded.
Calling UpdateLayout() on the view.
Calling InvalidateArrange() on the view.
Calling InvalidateMeasure() on the view.
Calling InvalidateProperty(FrameworkElement.WidthProperty) on the view.
Calling InvalidateVisual() on the view.
I have searched and searched and do not see any code anywhere touching the view's Top or Width properties (other than the style bindings).
Here's the style:
Sorry I had to blank out type names and some other things—it's a company application. If it helps, the main window/the view is at the end of a long inheritance line with Window as its great-great grandaddy. I'm trying to make the main window more reusable by MVVM-ing it—formerly these layout properties were set in code-behind in the view, and the view had constructor parameters :'( That's related to why I need to key the style, and why the style is based on other stuff. But none of the inherited types manipulate layout properties.
P.S. I've seen other people complain about how hard it is to resize WPF's Window. The most commonly suggested solution is to bind MinWidth and MaxWidth as well as Width. When I do that then the Width is indeed forced to the value I want, but you can't resize the window, the Width property still has its Local Value Source, and Top is still incorrect.
Given that "[Top] cannot be set through a style", and given the complications with binding Window.Width, I solved this a different way.
I created this attached property called WindowLayout and bound it to a viewmodel property in my style:
public static class WindowLayoutBehavior
{
public static readonly DependencyProperty LayoutProperty = DependencyPropertyHelpers.RegisterAttached(
(Window x) => GetLayout(x),
new PropertyMetadata(HandleLayoutChanged));
private static void HandleLayoutChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is Window window))
return;
if (!(e.NewValue is Rect rect))
return;
window.Height = rect.Height;
window.Left = rect.Left;
window.Top = rect.Top;
window.Width = rect.Width;
}
[AttachedPropertyBrowsableForType(typeof(Window))]
public static Rect GetLayout(Window window) =>
window.GetValue(LayoutProperty) is Rect rect
? rect
: default;
public static void SetLayout(Window window, Rect rect) =>
window.SetValue(LayoutProperty, rect);
}
DependencyPropertyHelpers.RegisterAttached is a shorthand helper method for creating the attached property in the way you might expect.
Usage in the style:
<Setter
Property="WindowLayoutBehavior.Layout"
Value="{Binding WindowLayout, Mode=OneWay}"/>
Now when I Snoop the app, Height, Left, Top, and Width all show as having Local Value Sources, and they change when the viewmodel property changes, so that works for me.

Get instance of another element via Xaml Binding (Attached or DependencyProperty)

I have a use case where I want to get at a reference to an element in Xaml from another element.
For instance, consider this simplistic case. I have a UserControl called A and a UserControl called B,
and somehow I want to register an attached property where B can get the reference to A.
e.g.
<MyCustomControl Name="A"/>
<MyCustomControl Name="B"
AttachedPropClass.TheOtherControl="{Binding ElementName=A}"/>
So I would expect B.TheOtherControl to be equal to A. Is this possible in Xaml? Note I am not binding to a property of A, but rather I want the whole element.
Any solution using DependencyProperties, or AttachedProperties or Behaviors that lets me do this in Xaml would be great.
Edit: I'm attempting to do this in both WPF and Silverlight4. I have tried the above and it doesn't work, a property changed callback on the Attached property never gets hit.
You could use a Behaviour with a property that you set to the Control name and then search the logical Tree for the control. I have a similar thing where I want a certain event on one control to move focus to another control. I do this by specifying the control name to the Behaviour.
<TextBox Name="A"/>
<TextBox Name="B">
<Interactivity:Interaction.Behaviors>
<Behaviours1:ProgressNextOnEnterAction NextTextBoxControlName="A" />
</Interactivity:Interaction.Behaviors>
</TextBox>
Ok I think I figured out what happened.
Declare your dependency property like this:
private static readonly DependencyProperty TheOtherControlProperty =
DependencyProperty.RegisterAttached(
"TheOtherControl",
typeof(MyCustomControl),
typeof(AttachedPropClass),// Change this part
null);
public static MyCustomControlGetTheOtherControl(MyCustomControltarget)
{
return (MyCustomControl)target.GetValue(TheOtherControlProperty);
}
public static void SetTheOtherControl(MyCustomControltarget, TextBlock value)
{
target.SetValue(TheOtherControlProperty, value);
}
I think the issue is that you set the OwnerType of the Dependency Property to MyCustomControl instead of to AttachedPropClass.
I've created an example that works.
Give this a try and let me know if I'm right.
u_u

How to change TextBlock Text from codebehind?

I have a custom control. There is a Stack Panel with Button and TextBlock in generic.xaml:
<StackPanel>
<TextBlock x:Name="StatusText" />
</StackPanel>
Then I have
public class MyClass : Control
{
// Constructor etc.
public static readonly DependencyProperty StatusTextProperty = DependencyProperty.Register("StatusText", typeof(TextBlock), typeof(MyClass), null);
public TextBlock StatusText
{
get { return (TextBlock)this.GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
}
There is if with some logic in that happens after the button is clicked.
How do I change the Text property of TextBloc?
I thought that I can do something like this
StatusText.SetValue(TextBlock.TextProperty, "Some text here.");
But it always returns NullReferenceException (Object reference not set to an instance of an object.)
Should I use PropertyChangedCallback() on dependency property or what else do I need? I am missing something ;-)
You're taking the wrong approach - instead of trying to push the text into the text block from the control's class, you need the text block to pull the value from the control's class. The main steps you need to do are:
Change the type of the dependency property from TextBlock to string.
Bind the Text property of the TextBlock in your control template to the dependency property using a TemplateBinding binding expression. Something along the lines of:
<TextBlock Text="{TemplateBinding StatusText}" />
You can then simply set the text to be displayed to the property on your control.
Hope this helps...
Chris
You can type your question on google and find answer few times faster.

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.

Two-way-like binding between a Resource's property and a runtime object property

my problem is the following :
I have defined a TextBox as a child of a ToolBar in a ResourceDictionary (x:Key MyToolbar). When my application loads, it places the ToolBar correctly inside the Window frame, along with its TextBox. So far, so good.
Of course, I'd like that very TextBox to be two-way databound to some objects' properties that are NOT defined in any ResourceDictionary.
More precisely, when the TextBox is all set in the correct window frame, and then, after the “Open” command a certain file is loaded, a Deserializer builds DesignerCanvas object using values from out of that file, in my case it is a string “Token” CLR property of a class that implements INotifyPropertyChanged.
Here some simplified code snippets. I will leave many blanks for clarity’s sake:
Class DesignerCanvas : INotifyPropertyChanged
{
Private string m_token;
Public string Token
{
Get{….
Set{ if (value…)
OnPropertyChanged(“Token”);
}
//notice there is no Constructor other than the default one
}
And on the XAML side I have something like this:
<ToolBar x:Key=”MyToolbar…..
<TextBox …
Now, my two goals are: to have the “static” TextBox resource on my toolbar pick up the values of the DesignerCanvas’ “Token” property as soon as the property changes (i.e. gets a value for the first time, basically), and similarly, and more importantly, I wish to make it possible to have the DesignerCanvas read the values I could put in manually into the TextBox and fill its Token Property with that user-input text (I think I will opt for the TextBox’ LostFocus Event as a trigger for the string value being passed/bound onto the DesignerCanvas’ “Token” Property).
Anyway, I’m not sure how to set up a perfect two-way (or two-way-like) DataBinding between the TextBox' Text property and the DesignerCanvas' Token Property, since we have one static resource (I’m not sure if static is the correct word), and another dynamic runtime object (again not sure if runtime or dynamic are the words).
How do I achieve this? Do I absolutely need to register a “Token”- DependencyProperty in DesignerCanvas? Do I absolutely need to have a XAML for the DesignerCanvas defined somewhere (for example in my Window1.xaml or a dummy s:DesignerCanvas resource along with the TextBox)?
Help appreciated!
Have you tried databinding with your textbox...
<TextBox Text="{Binding Path=Token, Mode=TwoWay}" />
...then when your app loads and places the ToolBar in the window frame, make sure that it also sets the DataContext property of the ToolBar to the instance of your DesignerCanvas class?

Resources