I have an object and one clone of the same object. First one is added in one panel(canvas) as a child and the second one is added to another panel as a child. Now, if I am doing some operations(say delete) on the main object, what is the best way in which the clone object also get notified(deleted). Basically, I am looking for how to create a link between a object and it's clone?
Thanks
Given the details, if you want both to always be in sync then I would rather suggest to 'avoid the clone'. Make the two work on the same instance, unless you have better reason to maintain the clone.
If there is some set of properties that you want to sync, then consider moving those to a different class and make the two (original and clone) share that instance.
Something like this:
public class PartiallyCloneable{
public SharedValue Shared{get;set;}
public ClonedValue Cloned{get;set;}
public PartiallyCloneable Clone(){
//deep copy ClonedValue, return same instance of SharedValue
}
}
public class SharedValue{
}
public class ClonedValue{
}
There's no simple direct way to do that, and adding the same object to multiple parents will throw exception. So you need to write a few lines of code. My guess is your approach is somehow a difficult one and you need to rethink the problem. Let me give you some ideas:
If you're using MVVM pattern (or you want to choose the standard path), define a ViewModel (which should derive from DependencyObject) for your object and bind the DataContext of both objects to one instance of this ViewModel. So that both View objects reference the same ViewModel. Then use Commands to do stuff to either objects.
If you don't want to go that far, use the same style for object and its clone, and set event handlers inside that style.
<Style x:Key="myStyle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="1" BorderBrush="Black" Unloaded="objectUnloaded">
<StackPanel>
<TextBlock Text="something"/>
<Button Content="delete" Click="deleteButtonClicked"/>
<Button Content="otherStuff" Click="otherStuffButtonClicked"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Also bind the Tag of each object to other.
<Control x:Name="main" Style="{StaticResource myStyle}"
Tag="{Binding ElementName=clone}"/>
<Control x:Name="clone" Style="{StaticResource myStyle}"
Tag="{Binding ElementName=main}"/>
Now if you implement those event handlers in code behind, you can access both object and clone at the same time:
private void otherStuffButtonClicked(object sender, RoutedEventArgs e)
{
var obj = sender as FrameworkElement;
var other = (obj.TemplatedParent as FrameworkElement).Tag;
//do stuff to obj
//do stuff to other
}
Related
Several people have asked how to split up large XAML files into smaller more manageable or readable chunks. In my case I have a XAML file with 10 tabs and each tab has a lot of complex controls. So the XAML file for this is huge and hard to read.
The "standard" answer for this seems to be User Controls.
I'm sure this is a real noob question, but if all you're trying to do is split up the XAML file, how do you do it without splitting up the C#, too? When you create a WPF user control Visual Studio creates a new XAML file plus new code-behind file to go with it and handle the events.
What I really wanted was the equivalent of a "C# partial" directive for XAML so I could just split it up among multiple files but have the events handled in one place. How close can I get to that?
Thanks in advance!
There is no way to do as you describe (to allow multiple xaml files to share the same c# code). Also from what you describe there isn't an easy way to quickly abstract that code without having to make some changes. Since wpf events are typically driven by commands, the best solution would probably be to change your events to fire commands rather than putting the logic within the actual event handler itself, then calling it from the user control would be trivial.
But before you go changing all your code, you may be able to abstract out a lot of the long stuff using styles, which are way easier to abstract out and shouldn't mess with your events. So if you notice repeating how things are setup across lots of controls, just declare all of it as a style and you can move it into a resource dictionary elsewhere to remove some clutter.
To elaborate a bit you can use styles not just to stop repetition, but also to abstract out how you define your controls (much like you are trying to use user controls for, you can also define events there). For example...
<Style TargetType="TabItem" x:Key="Tab1Style">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<!--Note even if this style is defined in a resource file the
events will still be tied to the class of the control
using the style-->
<Button Click="Button_Click"/>
<Button Click="Button_Click_1" />
<Button Click="Button_Click_2" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then simplify one of your tabitems to simply...
<TabControl>
<TabItem Style="{StaticResource Tab1Style}" />
</TabControl>
If you really have your heart set on user controls you could also just route all the events out. Something like this...
<UserControl ...>
<Button Click="OnClick"/>
</UserControl>
public partial class UserControl1 : UserControl
{
public static readonly RoutedEvent ButtonClick = EventManager.RegisterRoutedEvent(
"ButtonClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl1));
public event RoutedEventHandler ButtonClickHandler
{
add { AddHandler(ButtonClick, value); }
remove { RemoveHandler(ButtonClick, value); }
}
private void OnClick(object sender, RoutedEventArgs e)
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(UserControl1.ButtonClick);
RaiseEvent(newEventArgs);
}
public UserControl1()
{
InitializeComponent();
}
}
<Window>
<local:UserControl1 ButtonClickHandler="Button_Click" />
</Window>
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Click");
}
(like I said lots of plumbing code)
Why won't this work?
In generic.xaml for a custom control:
In the style applied to the custom control...
<Setter Property="ChromeContent">
<Setter.Value>
<Grid />
</Setter.Value>
</Setter>
...
Later, in the control template...
<ContentPresenter Grid.Column="0"
x:Name="ChromeContentPresenter"
Content="{TemplateBinding ChromeContent}" />
Here's the dependency property for ChromeContent...
public Object ChromeContent
{
get { return (Object)GetValue(ChromeContentProperty); }
set { SetValue(ChromeContentProperty, value); }
}
public static readonly DependencyProperty ChromeContentProperty =
DependencyProperty.Register("ChromeContent", typeof(Object),
typeof(casPopup), null);
As you can see, it takes any object. I tried changing it to a Grid, but that did not help.
It throws this error (from javascript): _Failed to assign to property 'System.Windows.Controls.ContentPresenter.Content'
Oddly, the following will work fine if I remove the Grid from the setter nd just use text:
<Setter Property="ChromeContent" Value="DEFAULT" />
Also, this will work too from the OnApplyTemplate method in the control class:
Grid g = new Grid();
g.Width = 100;
g.Height = 25;
g.Background = new SolidColorBrush(Colors.LightGray);
ChromeContent = g;
I'm having a hard time understanding what is preventing the default content of a grid, defined in the generic.xaml from working. Does anyone have any knowledge on this matter?
Many thanks in advance for your help!
This is the problem:-
<Setter Property="ChromeContent">
<Setter.Value>
<Grid />
</Setter.Value>
</Setter>
You should not include a UIElement directly in a resource dictionary or as a value of a style. You might see the style as being some kind of descriptor but it isn't. The values in a style are constructed instances of the objects they hold. Your style holds a single instance of Grid. Whenever that style is used to assign to a ChromeContent property it will attempt to assing the same single instance of the Grid.
A UIElement can only be a child of one parent. What would happen if two instances your control were constructed? There would (if silverlight let you) be an attempt to assign the same single instance of the Grid to both controls.
This is one reason for templates such as ControlTemplate and DataTemplate. The markup inside these is invoked each time the template is used rather than when the Xaml is first parsed.
Edit:
To answer you supplementary question, you should default another property of type DataTemplate:-
<Setter Property="ChromeContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid />
</DataTemplate>
</Setter.Value>
</Setter>
Property:-
public Object ChromeContentTemplate
{
get { return (DataTemplate)GetValue(ChromeContentTemplateProperty); }
set { SetValue(ChromeContentTemplateProperty, value); }
}
public static readonly DependencyProperty ChromeContentTemplateProperty=
DependencyProperty.Register("ChromeContentTemplate", typeof(DataTemplate),
typeof(casPopup), null);
Control Template:-
<ContentPresenter Grid.Column="0"
x:Name="ChromeContentPresenter"
Content="{TemplateBinding ChromeContent}"
ContentTemplate="{TemplateBinding ChromeContentTemplate" />
I'm looking to set a UserControl to be the Content of another UserControl in XAML, in the same way you can set a Button's Content to be anything.
Let's say my "outer" UserControl looks like this:
<MyUserControl>
<Grid>
<Border FancyPantsStyling="True">
<-- I want to insert other controls here -->
</Border>
</Grid>
</MyUserControl>
And I'd like to instantiate this way:
<local:MyUserControl>
<local:MyUserControl.Content>
<local:AnotherControl />
</local:MyUserControl.Content>
</local:MyUserControl>
How do I design MyUserControl to render it's Content in a specific location?
All the stuff you put into your UserControl's XAML is its Content so you can't inject something else by setting the Content property. There are a few different ways you could handle this. If you don't have anything in the code-behind for MyUserControl you can just get rid of it and use something like:
<ContentControl>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Border FancyPantsStyling="True">
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</ContentControl.Template>
<local:AnotherControl/>
</ContentControl>
If you have code behind that doesn't access the XAML elements directly you can do a similar thing with your existing control (since UC derives from ContentControl):
<local:MyUserControl>
<local:MyUserControl.Template>
<ControlTemplate TargetType="{x:Type local:MyUserControl}">
<Grid>
<Border FancyPantsStyling="True">
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</local:MyUserControl.Template>
</local:MyUserControl>
If you need to keep the existing content connected to your code-behind you can use a DataTemplate to pass in the external content (into a new DP on MyUserControl) and apply that template to a ContentControl in the UC's XAML.
I got an idea, then tried it and it worked for me. I just wanted to share this to other people. I hope it will be useful.
The video link which explains what is the end of the solution: Video Link
The basic idea is to create UIElement DependencyProperty instead of creating Border DependencyProperty
Firstly, you should add your borders or panels or whatever you want to your user control (in your case it'S "MyUserControl") and make sure it has a name to access from .cs file:
<Border x:Name="LeftBorder" Grid.Column="0">
Then you should add a public UIElement value to your user control (in your case it's "MyUserControl"):
public UIElement LeftBorderChild
{
get { return (UIElement)GetValue(LeftBorderChildProperty ); }
set { SetValue(LeftBorderChildProperty , value); }
}
Secondly, type of your Dependencyproperty must be UIElement:
public static readonly DependencyProperty LeftBorderChildProperty = DependencyProperty.Register("LeftBorderChild", typeof(UIElement), typeof(MyUserControl), new PropertyMetadata(new PropertyChangedCallback(LeftBorderChildChanged)));
After these, typing events:
public static void LeftBorderChildChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyUserControl thisUserControl = d as MyUserControl;
thisCombobox._LeftBorderChildChanged(e); // Calling local event. The new child will be added in this local event function.
}
public void _LeftBorderChildChanged(DependencyPropertyChangedEventArgs e)
{
// In this function, new child element will be added to inside of LeftBorder
this.LeftBorder.Child = (UIElement)e.NewValue; // Sets left border child
}
We're done with this class. Let's call it from other class and add a control inside of it.
<local:MyUserControl Width="312" HorizontalAlignment="Right"
Margin="48, 0, 0, 0" VerticalAlignment="Center"
Height="56" >
<local:MyUserControl.LeftBorder>
<-- You can insert another control here -->
<-- Just don't remember that if you want to add more than one controls, you should add a panel then add controls into inside of the panel because Border child can only 1 child item -->
<StackPanel>
<-- Now you can insert your controls -->
</StackPanel>
</local:MyUserControl.LeftBorder>
</local:MyUserControl>
Note: When you do this firstly, you have to run your program before viewing in xaml designer. After running your program, all design systems are going to run synchronously.
I hope i understood what you mean and answered correctly.
Thank You
unless i misunderstood the question, you can use in your control and set its content to whatever you need.
OK, so the situation is I'm defining an ItemTemplate for a ListBox in a ResourceDictionary (Styles.xaml). The ListBoxItem Template looks something like this:
<ControlTemplate TargetType="ListBoxItem">
<Button Command="{Binding Path=DoSomeCommand}" Content="Test" />
</ControlTemplate>
Now wherever this template is used, I'd like to have this button's click bind to an available ViewModel command to handle it.
However this does not work as is, I've also tried this:
<ControlTemplate TargetType="ListBoxItem">
<Button Command="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DoSomeCommand}" Content="Test" />
</ControlTemplate>
But still no dice.
A simple example that does work is if you define the template in the control (resources) that is using it, and just use an event handler (the same handler for all generated XAML.
Any ideas or thoughts on the best way to accomplish this? I figure this must be a common scenario: the goal is just to allow the user to interact with the items in the ListBox.
Thanks!
OK I think I answered my own question :
The solution seems to be to use 'nested' ViewModels here:
In other words, rather than have my ListBox bind directly to a collection of DTOs/business objects (as I was doing above) I instead created a simple ViewModel to wrap each DTO, and have the command on it, rather than on the original, top-level VM.
So the bound collection now looks like this:
TestItems = new ObservableCollection<ItemVM> ()
{
new ItemVM(),
new ItemVM(),
new ItemVM()
};
And each ItemVM just wraps the DTO, and has the command:
public class ItemVM : INotifyPropertyChanged
{
public ItemVM ()
{
this.MyCommand = new DelegateCommand<string> ( TheCommand );
}
public ICommand MyCommand { get; private set; }
public MyBusinessObject BizObj;
}
And voila, no need for a RelativeSource, and we have a reusable template complete with commands.
Long answer: Reference to a TextBox inside a DataTemplate
Short answer: Use Prism Commands or Blend Behaviours.
I have recently started investigating the MVVM pattern with WPF for an upcoming project. I started with Josh Smith's MSDN article. I have a question (well many, but let's start with one):
I have an IndividualViewModel which exposes the properties of the model. I need two views "Add Individual" and "Edit Individual" which are very similar as you can imagine. What I have done currently is to have 2 subclasses AddIndividualViewModel and EditIndividualViewModel which expose the Add and Edit commands respectively. I also have 2 similary named views that bind to these.
Now this method works and these classes are fairly small anyway, but I'm wondering if it is possible for me to have just the one view model, which exposes both commands. I would still have 2 views which would bind to this same view model, exposing the appropriate command as a button. I'm not quite sure how to do this. In the main window resources I have something like:
<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
With this method of binding you can only have a one-to-one binding, i.e. the same view is always shown for a given view model. Is there a way to automatically switch the view depending on a property on the view model (e.g. IndividualViewModel.Mode). Is there a different approach I should be considering?
Note that the main window has a collection of view models and shows each in tab.
Thank you!
So you need 2 different views based on a property value. One thing to consider is to refactor your presentation code, so instead of the values of a property you could have real subclasses. Then you can use 2 different DataTemplate for each class.
<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:EditIndividualViewModel}">
<Views:EditIndividualView />
</DataTemplate>
If you think that is an overkill, you could use a trigger and wrap your specific views into a ContentPresenter.
<DataTemplate x:Key="AddIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
<DataTemplate x:Key="EditIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
<Views:EditIndividualView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:IndividualViewModel}">
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="ContentPresenter">
<Setter Property="ContentTemplate" Value="{StaticResource AddIndividualTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Mode}" Value="{x:Static ViewModels:IndividualMode.Edit}">
<Setter Property="ContentTemplate" Value="{StaticResource EditIndividualTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</DataTemplate>
Thanks for pointing me in the right direction! I am still new with WPF too and learning about all the different possibilities including binding methods. Anyway for anyone interested, here is the solution I arrived at for this particular case:
I decided I wanted to keep the view models separated in two subclasses AddIndividualViewModel and EditIndividualViewModel which only expose commands, rather than trying to manage state in the one class. However I wanted one view so that I'm not duplicating the XAML. I ended up using two DataTemplates and DataTemplateSelector to switch out the action buttons depending on the view model:
<DataTemplate x:Key="addTemplate">
<Button Command="{Binding Path=AddCommand}">Add</Button>
</DataTemplate>
<DataTemplate x:Key="editTemplate">
<Button Command="{Binding Path=UpdateCommand}">Update</Button>
</DataTemplate>
<TemplateSelectors:AddEditTemplateSelector
AddTemplate="{StaticResource addTemplate}"
EditTemplate="{StaticResource editTemplate}"
x:Key="addEditTemplateSelector" />
and a content presenter at the bottom of the form:
<ContentPresenter Content="{Binding}"
ContentTemplateSelector="{StaticResource addEditTemplateSelector}" />
Here is the code for the template selector:
class AddEditTemplateSelector : DataTemplateSelector
{
public DataTemplate AddTemplate { get; set; }
public DataTemplate EditTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is AddIndividualViewModel)
{
return AddTemplate;
}
else if (item is EditIndividualViewModel)
{
return EditTemplate;
}
return null;
}
}
This may or may not be how implement the final thing (given the requirements) but it's good to see I have this sort of option available.
There's no reason why you shouldn't be able to achieve that. One way of doing this is to provide some flag in your view model stating whether you're in add mode or in edit mode, and styling your view based on that flag using simple bindings, triggers or template selectors.
For reference you may look at Sacha Barber's DataWrapper class that's part of his Cinch framework (not directly applicable to your case, but it's a good starting point) which wraps data fields in the view model in such a way to support a flag to toggle between read only (view record mode), and read-write (edit record mode). You could apply a similar approach to make the distinction between add and edit.
Basically, instead of having simple properties in your view model, instantiate a data wrapper class which includes a Value property, and a IsAdding property. In your view, you can use bindings, triggers or template selectors to modify templates based on that property.
For this task you do not need any DataTemplateSelector at all.
Derive both EditIndividualVM and AddINdividualVM from IndividualVM.
The Edit- and AddCommands route to a setter property in the IndividualVM.
The setter VM = new AddIndividualVM or VM = new EditIndividualVM depending on which button is pressed.
In xaml you bind in the contentgrid to your VM property like this: