I have a (moveable) dialog window whose initial position I want to offset from the right hand side of the app (its parent container is Canvas) based on whether some static windows are open or not.
As such I need something along the lines of this
<MyWindow Canvas.Right="{Binding Element.Width + Element2.Width}" />
This wouldnt entirely work though as if Element1 wasnt visible I wouldnt want it in the calculation. Perhaps the logical conclusion is that this cannot be done via binding alone and I'd need a property on the ViewModel.
This raises another issue in that each one of these elements (including the parent window) has its own ViewModel. Would it not be against best practices for one of these ViewModels to interact directly with Views to calculate this information?
You can use MultiBinding and calculate the total width in an IMultiValueConverter.
The ActualWidth property of an element should be 0 if it is collapsed, so you can use the element's ActualWidth property for the binding.
Related
I have custom FrameworkElement (displays 3D model, and allows to control camera), and i need to display multiple items of this type simultaneously (they should be overlapping). Items are stored in list.
For now, i can achieve that by simply placing few ContentControls in Grid, and using my FrameworkElements as Content. And it works well. But, any attempt to achieve that result for custom ammount of elements fails. I've tried to use ItemsControl, but it just doesn't work.
Is there any convenient way to display multiple items from source list, placing them in single position?
Update
Canvas and ItemsControl are simply not working for me. Even if i simply move single element there.
That means,
<ContentControl Content="{Binding DrawableElements[0]}"></ContentControl>
works well, but
<ItemsControl>
<ContentControl Content="{Binding DrawableElements[0]}"></ContentControl>
</ItemsControl>
doesn't work at all, although, it's pretty basic approach. Same goes to Canvas. I don't know, if i'm using it right, but i just can't understand, why simple ItemsControl with single element doesn't want to be displayed.
Update #2
If i have class, which has certain properties, with FrameworkElement among them, my ItemsControl, which uses list of this class instances, will work just fine with any properties EXCEPT FrameworkElement. Now i really can't understand what happening.
Note:
I'm trying to follow MVVM pattern, so there should be no direct impact on UI from code. That's why I've chosen ContentControl in first place. Otherwise i might simply add new FrameworkElements as children to simple Grid.
The problem was, thas ContentControl inside ItemsControl was setting its size params to 0 by default, so, basically, my FrameworkElements were just zero sized. I had to specify Widht and Height manually to make it visible.
I'm using the Telerik WPF RadGanttView control to display a bunch of data. Since the data can be arbitrarily weird - events that are instantaneous, or that last days, or have a week between them - it's not possible to set a PixelLength (i.e. a scale factor) that's guaranteed to be useful. I've been asked to make it possible to change this scale factor with a slider. Unfortunately, just changing the scale factor with the slider has a usability issue, which I need to fix by manually adjusting where the view is scrolled. I'm at a loss as to how exactly.
To outline the usability problem I'm fixing: the view works by providing a "viewport", located at an "offset", over the whole "extent" of the data set. (Terms lifted from the Telerik API. These seem to simply represent pixels in the canvas underlying the scrollable view.) When the scale factor goes from, say, 100% to 200% (which corresponds to halving the PixelLength), what happens is that the width of the extent is doubled, but the horizontal offset remains the same. The consequence is that after zooming in, it's quite likely you'll see entirely different data than before, since the events that were there before got "pulled out" of the viewport to the right.
The way I intend to fix this is: grab the offset/viewport/extent etc before zooming in, zoom, then do some maths I haven't figured out yet with that and the new offset/viewport/extent. The problem is: the properties of RadGanttView that describe the scrolling stuff are not DependencyPropertys, and I cannot simply bind them to properties on my ViewModel. (In fact, they're not even accessible in XAML to begin with, RadGanttView implements IScrollingInfo explicitly.)
So, my question is: how do I, in my ViewModel, or wherever else in reaction to the ViewModel's scale factor changing, access properties of a control in the corresponding View, that cannot be data-bound? Every search I tried tells me that accessing the view from the viewmodel is "not MVVM", but since Telerik is a third-party library, I can't really refactor how this works on their side.
A fill-in-the-blanks outline of my code:
FooViewModel.cs
class FooViewModel
{
// A slider in the view pushes its value into this property
double ScaleFactor
{
get { /*...*/ }
set
{
PixelLength = GetNewPixelLength(value);
// ...
}
}
// The RadGanttView pulls its scale from this property
double PixelLength
{
get { ... }
set
{
// How do I get these values?
var oldOffset = ???;
var oldExtent - ???;
// Trigger the actual change in the view.
PropertyChanged("PixelLength", ...);
var newExtent = ???;
???.HorizontalOffset = GetNewOffset(...);
}
}
}
FooView.xaml
<UserControl ... d:DataContext="{d:DesignInstance my:FooViewModel}">
<telerik:RadGanttView x:Name="Gantt">
<!-- ... -->
</telerik:RadGanttView>
</UserControl>
Things I've looked into:
Making a bunch of DependencyProperty wrappers in the FooView code-behind that just access corresponding properties in the RadGanttView. This seems like it both is a horrible abuse of the system - i.e. it doesn't seem to make sense to have a dependency property not backed by a DependencyObject. And also that it plain wouldn't work - in WPF, the view seems to "push" data into the view model, and I'd still have no way to actually get the current values, since the values of the wrapper properties would never get updated.
Uh, Commands, maybe? I'm fairly new at WPF, I have no clue how those work at all, merely a vague impression that they might be a loosely coupled way for the view model to talk to the view.
Attached properties? Custom bindings? Way above my pay grade, if they help I don't know how myself. It seems like they could accomplish the "dirty" solution of just binding a control to a view model property. Since the type of that property would be IScrollingInfo, not the whole view, I could live with that.
Attached Behaviors may solve your issue. They are basically Attached Properties with a callback.
Check out my answer here. Just instead of KeyDown event, you register to the Changed event (or whatever your control is actually calling it) and then assign the value you get from the Changed event to your Attached Property, and you have two way binding on a non-bindable property
I guess I'm a little confused as to whether properties like display range should be placed in the model (that gets inherited as a datacontext so that subcontrols can bind to it easily)or whether I should have properties be placed in the graphviewer class, and then let the components that need access to it have their own properties that they bind to the ancestor instead. Is it cleaner to bind to an ancestor control or just to bind off the model? I feel like the latter is cleaner, but then display range is pretty clearly a property of the view.
For example. I have a property AxisdivisionUnit that is needed in a scrollviewer, as well as used by a few thumbs to recalculate position on graph updates. The scrollviewer only appears when a treeview in the top level control (graphviewer) is populated. So I could either put the property axisdivisionunit on the graphviewer and bind the property to properties in the scrollviewer and thumb. Or I could have the thumb and scrollviewer bind to properties in the model (viewmodel if i were better at separating the UI out entirely.
Let me see if I can help..
First off, since you are discussing mainly the presentation of what things look like on your UI, then I do not think that the property should be in your model at all. The real question is whether it belongs in your View or ViewModel.
AxisDivisionUnit, sounds like it is only part of how the graph looks. I'm thinking that it would make more sense for that to be in the view only. If you had some properties describing the limits of your graph that were tied to business logic, then something like that may be better off in the ViewModel since you could possibly want to test that code and if you were to replace the UI you'd still want to enforce those exact same limitations.
I guess ask yourself, "If I were to replace this graph with a totally different graph and UI to display the same data, would I have to enforce this same logic?" If the answer is no, that it is just how you want to display it for this case... then it belongs in the View and you can bind a Control's property to another control's property or use triggers, behaviors, etc. to implement it in the View.
Suppose I have an element (in my case, a StackPanel) that contains several UI elements (in my case, lots of textboxes contained in various Grids contained in etc.etc. contained in the StackPanel).
I want to know whether any one of those textboxes has focus. (I want to bind this property to a View-Model property.) Is there a property for this? If not, what is the simplest way to bind to this kind of information, without having to first extract all the textboxes? (They’re generated by templates.)
You could use IsKeyboardFocusWithin. What kind of binding are you wanting to do to it? If it's something simple like you're wanting to change the background of the stackpanel if a textbox within has focus, you should be able to use this as a style trigger.
I have written a custom Silverlight control based on Control. I have two DependencyProperties called Top and Bottom which both hold child controls for a specific layout display. I then use a ControlTemplate to arrange these two controls into a grid, placing one on the 0 row and the other on the 1 row. The problem I have is that I cannot seem to figure out how to get each child control's Parent property to point to my custom control. When I inspect each control at run-time, the Parent property of each is null.
This is a simple example, but I think you can see the general problem. I have a number of more complex controls that all share this problem. I know there is some magic I am missing. If a ContentControl's Content property is set to some child it is somehow setting that child's parent to itself.
Edit: A little more info
In WPF, one might use functions like AddVisualChild(), RemoveVisualChild(), AddLogicalChild(), RemoveLogicChild() to manage parent/child relationships, but these functions are not available in Silverlight.
After quite a bit of research I believe that this is not possible. I was able to recurse through the Visual Tree instead of the Logic Tree using the VisualTreeHelper to accomplish my ultimate goal.
The Parent property cannot be arbitrary, it reflects the real parent of the control for use when rendering.
From MSDN:
Parent may be a null reference (Nothing in Visual Basic) in cases where an element was instantiated, but is not attached to any logical tree that eventually connects to the page level root element, or the application object.
...
Changing an element's parent is typically only done through manipulation of collections, by using dedicated add or remove methods, or through setting content properties of elements.