I'm trying to access a user control which is inside the control template of a content control. Specifically:
<ContentControl x:Name="MyList" >
<ContentControl.Template>
<ControlTemplate x:Name="MyControlTemplate">
<Border RenderTransformOrigin="0,0" x:Name="border">
<UserControls:MyControl x:Name="MyControlName" Width="100" ViewModel="{Binding}" />
I can access this.MyList but it says this.MyControlName is not found. How do I access the MyControlName object from code-behind in this situation?
Thanks!
You need to get the template and locate the control by name on the templated control, something like:
var template = MyList.Template;
var myControl = (MyControl)template.FindName("MyControlName", MyList);
Templates are just that: Abstract descriptions of what is to be created, the controls in templates only exist in the context of something that is being templated.
Note that you should only ever access the elements within a control template if you are authoring the control that the template is for. Access from outside should be done via bound properties and methods.
For data templates this is similar. All the things you need to access should be bound to an object and access should then be through said object. This is especially true in cases of item controls which virtualize their items, so the elements do not even exist most of the time.
U also can get control from every template by adding Loaded event in control and then in code assign sender of event to some variable.
Related
I am reasonably proficient in XAML and WPF having trouble with binding to an additional control within an extended control from outside the control. Sorry, that's a real mouthful so let me explain:
I have a control that I have extended from a ComboBox and applied the template and overridden the property metadata and all that stuff and re-templated it so it looks and works as I want it to. Now, I want to add a TextBox to provide search functionality for the ComboBox which I have exposed dependency properties to determine if it is visible or not and added this to the first row of the Grid above the ItemsControl and all works fine. I have added a dependency property called IsFiltered and applied a template binding to determine if the filter is visible and from outside my control I can set this value and it all works.
However, I have added a dependency property to the extended ComboBox (MyComboBox if you like) as a string property so that I can assign a filter text property from my view model that will eventually work its way to the text box embedded within the control. The TextBox in the conrol is also bound using {TemplateBinding FilterText} dependency property, as it hooks back to my MyConboBox control and the assignment is accepted and recognised. However, while the property from my view model is set and read and interacts with the FilteText property in MyComboBox to which the TextBox inside by MyComboBox control template is also bound to, the TextBox does not trigger a change.
<TextBox
Grid.Row="0"
Margin="4"
Text="{TemplateBinding FilterText}"
BorderBrush="Red"
Visibility="{TemplateBinding IsFiltered, Converter={converters:BoolToVisibilityConverter}}"/>
Can anyone help?
I am moving over from WinForms to WPF and trying to implement the MVVM pattern for a touchscreen application. I created several custom controls inside a WPF Control Library (dll), and I can bring these controls into the View with no issue. However, I am getting stuck on a purely academic scenario where I want a TextBox inside the View to display my custom control's ToggleButton.IsChecked property as "Checked" and "Unchecked" respectively.
To sum up, I need to know the proper way to expose properties of a control that is inside a custom user control. Then when the exposed property changes update some other control with custom data based on the property that changed.
To sum up, I need to know the proper way to expose properties of a control that is inside a custom user control. Then when the exposed property changes update some other control with custom data based on the property that changed.
You're describing dependency properties. You need to add a dependency property to your custom control, which you then bind to from inside the control, and from outside it (in your view).
The first part will depend on whether you're using a UserControl or a Control. Let's say it is a Control, then you would use a TemplatedParent binding in your ControlTemplate:
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsToggleChecked,Mode=TwoWay}" ... />
If on the other hand it is a UserControl, then the approach is similar, but you need to make sure the data context is right. One approach would be to use a FindAncestor binding:
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=IsToggleChecked,Mode=TwoWay}" ... />
Now, to add the dependency property, try the Visual Studio code snippet "propdp". It should look something like this:
public bool IsToggleChecked
{
get { return (bool)GetValue(IsToggleCheckedProperty); }
set { SetValue(IsToggleCheckedProperty, value); }
}
public static readonly DependencyProperty IsToggleCheckedProperty =
DependencyProperty.Register("IsToggleChecked", typeof(bool), typeof(MyCustomControl), new PropertyMetadata(false));
And now finally you can bind your TextBox to the new dependency property:
<TextBox Text="{Binding ElementName=myCustomControl,Path=IsToggleChecked,Converter={StaticResource BoolToTextConverter}}" />
<local:MyCustomControl x:Name="myCustomControl" ... />
I assumed that you would want to make an IValueConverter "BoolToTextConverter" that converts the boolean value to the string "Checked" or "Unchecked".
I have the following (condensed) silverlight xaml for a view / viewmodel:
<UserControl x:Class=MyView>
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource MyViewModel}}">
<UserControl>
Hopefully this looks familiar to you all.
However, I would like to create 2 instances of this same view user control, but to pass in a parameter to the view model to allow me to have slightly different view model data based on a property which I pass into the view model. Something like:
<UserControl x:Class=MyView>
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel" Filter="Some value set at a higher level"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource MyViewModel}}">
<UserControl>
The problem is that I can't hard code the Filter parameter inside the user control, but need to set it at a higher level. Is there a way to obtain the filter parameter from higher up via binding, and what would the syntax look like. I was hoping something like the following:
Either directly from the parent something like:
<MyView>
<MyView.ViewModel Filter="All">
</MyView>
<MyView>
<MyView.ViewModel Filter="Some">
</MyView>
Or from user control looking upwards, something like:
<UserControl.Resources>
<MyViewModel x:Name="MyViewModel" Filter="{Binding FilterTypeFromDataContextHigherUpTheTree}"/>
</UserControl.Resources>
but I don't know if is is possible to directly refer to the static resource view model from the parent in order to set a property, or what the syntax would look like.
I also don't know if there is an easier way to do this, as I suspect my approach is not very elegant.
The real question is how can I pass a parameter into a view model which is a static resourd
Though it's possible to store the ViewModel as a resource, we usually see the ViewModel set as the DataContext of a view. Then, if someone needs to access the ViewModel through the View, they just cast the DataContext to the proper ViewModel type and access it's properties directly.
If you want to go the Binding route for your Filter you have a couple of choices. You can set the Bnding Soure to RelativeSource.TemplatedParent if the child control is placed inside of the parent control in a template (or Style). You could also use ElementName, but only if the named element is inside of the same scope (in your example above, the named element would have to be somewhere inside of MyView).
The final option I can think of would be to expose a DependencyProperty for the filter on MyView and then set the binding of the ViewModel to that property. This would effectively bubble the filter so that it's accessible outside of MyView, but I don't like this approach at all because it's adding properties to the View just for the sake of passing them to the ViewModel. That should never happen. The ViewModel should always be accessible independantly of the View, which is why I recommend exposing it through the DataContext property (inferred) or through a custom property specifically for the ViewModel (explicit).
I sometimes do this with my ViewModels (e.g., instantiate them as a resource). When I do this, I have my VMs extend DependencyObject, for reasons I'll detail later.
I'd recommend that you move them to app.xaml and define them as resources of the application, where they will be available to everyone as a resource of the application. That way you can
<UserControl
DataContext="{StaticResource MyViewModel}" />
In addition, you can bind properties of your ViewModels together. That's why I extend DependencyObject.
Of course, this limits you to a single instance of MyViewModel, which may or may not work in your case. If it doesn't work, I'd define a ViewModel which contains the shared data, then bind to this via a static resource, as in your "looking upward" example.
How can I create a control in silverlight that can be used as the layout root, but still have a "Template" property so I can wrap the users content inside another control using a style?
My current implementation is close, it takes the content that the user places in the control and wraps it but the user has to put a grid or panel in if there is multiple controls for the content.
--Update --
This is the code I'm using that will not work as the rootlayout for multiple children unless the user puts a grid around their content. If I inherit from Grid or Panel I get an error about the DefaultStyleKey property not being available.
public class BusyControl :ContentControl
{
public BusyControl()
{
this.DefaultStyleKey = typeof(BusyControl);
}
}
<Style TargetType="local:BusyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BusyControl">
<telerik:RadBusyIndicator DisplayAfter="0:0:0.5" IsBusy="{Binding IsBusy}" BusyContent="{Binding BusyMessage}">
<ContentPresenter Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
</telerik:RadBusyIndicator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is how I want the user to be able to use my new control with out having to wrap their content in a panel or grid.
<cdc:BusyControl x:Name="BusyControl">
<some:Control x:Name="Control1" />
<some:Control x:Name="Control2" />
</cdc:BusyControl>
Seems to me that what you want is to derive your control from an ItemsControl not a ContentControl. In any ControlTemplate that you use you can place the controls using an ItemsPresenter instead of the ContentPresenter you would have used in a ContentControl.
You can have your control inherit from Panel if you want it to be able to have multiple children. You will have to handle laying out the panel's child controls if you do this. See this MSDN article on creating custom panels.
You can then specify your Template and stick the user content into the template.
I think that if you want multiple controls, i.e. children, as content then you have to use some sort of panel, which is the base class for the .Children property.
Not sure if this is applicable to your situation. I had trouble grasping exactly what's going on in your question. Maybe you can make a custom user control that inherits from ContentControl. As you may or may not know, custom user controls need a default style key. With a custom user control you need to define a template in the default style. Now the template can have a ContentControl somewhere inside of it and its content property should be template binding to the contentcontrol.content property. Or you can override the OnContentChanged function and do whatever you want in that override function (like put a single object in the control by itself... or for multiple objects create a new grid/panel and then set the objects as the grid/panels children for the user and then do what ever it is you are doing with the grid/panel. You would have to set/bind the content property on your new control. Make sense?
I don't know about your error with the default style key, and i don't have any telerik controls, but couldn't you just inherit from your telerik busy indicator? Would something like this work for you, (or put you on the right track).
protected override void OnContentChanged( object oldContent, object newContent )
{
//I dont know how you are assigning content,
//but i would say if it's IEnumerable and count is > 1 it should use your panel
var newMultiContent = newContent as System.Collections.IEnumerable;
if ( newMultiContent!=null && newMultiContent.Cast<object>().Count()>1)
{
var myNewContentContainer = new StackPanel();//or grid or whatever
myNewContentContainer.Children.Clear();
//add children
foreach (var item in newMultiContent.OfType<UIElement>())
myNewContentContainer.Children.Add(item);
//instead of the old content that wasn't what you wanted, use the new content container
base.OnContentChanged( oldContent, myNewContentContainer );
//or maybe try this and call the base method at the beginning...
Content = myNewContentContaint
}
else
base.OnContentChanged( oldContent, newContent );
}
I have a an object created in Xaml:
<Grid>
<MyObject/>
</Grid>
I need someway to bind the object myObject back to a property in my view model. I dont know whether this is possible, everything ive seen so far binds properties together, but any help would be greatly appreciated.
I am assuming what you want is your ViewModel to hold the actual visual control MyObject in it and your Grid to display it via MVVM.
This is possible through ContentControl in WPF.
Assuming your ViewModel has a property MyObjectView which holds MyObject...
<Grid>
<ContentControl Content="{Binding MyObjectView}" />
</Grid>
Having said that you must take caution that same MyObjectView is not bound to any other content control as that will result in an error
"Specified element is already the logical child of another element.
Disconnect it first"
And if that requirement is possible then you must excercise ContentTemplate option.
Let me know if this helps.
It is possible. It kinda breaks mvvm though.
You can attach an InvokeCommandAction to this object, and bind the CommandParameter to it via ElementBinding. Then in the callback of the command which you defined in the viewmodel, you will have a reference to this object from the CommandParameter.