I have followed the Accepted Answer's instructions from this post as regards creating a code behind file for a Resource Dictionary, and it worked...so now I can attach events to controls in the generic.xml file.
But now I want to be able to call the DragMove() method from an event in there and since there aren't any references to the window hosting the dictionary at the time, I don't know how to call this DragMove() method.
So, from a Resource Dictionary Code behind file, is there any way I can make a reference to the window that will currently be hosting that Resource Dictionary?
[Update] (Temporary Solution)
As a simple (yet stupid) workaround, I have currently done the following:
Since I can reference the Application.Current.MainWindow from the Generic.xaml.cs code-behind, I now have this in the Generic.xaml.cs:
private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application.Current.MainWindow.DragMove();
}
And then I'm attaching PreviewMouseLeftButtonDown handler to each Window I have, like such:
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Application.Current.MainWindow = this;
}
It, well, it works...and until someone can come up with the proper way on how to do this, It should serve me well enough.
There is no way I know of doing this. However, if you're trying to determine the Window given a specific resource, you could use a RelativeSource:
<SolidColorBrush x:Key="MyBrush" Color="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource WindowToColorConverter}"/>
And if you're doing it from code, you can use Window.GetWindow(). You just need a DependencyObject hosted in that Window.
From architectural point of view I would say you are about to break the paradigm. It might be a bad decision providing Resource Dictionary with notion of UI that consumes it and giving some logic other than providing resources.
You might want some Adapter between UI and resource dictionary, or Controller if this is really needed to wire Resource Dictionary but again you shouldn't inject any logic in a resource container...
You can access your main window through
Application.Current.MainWindow
Hope this helps
Related
I have a WPF control ParentWPFControl from a third party that I would like to inherit from (let's call the child class ChildWPFControl). In the process, I plan to override some of the back-end logic and parts of the front end styles. I can do the former just fine but I have problems doing the latter.
I attempt to use a xaml <-> xaml.cs structure for the child country, but that appears to be not allowed with the following warning from VS:
Partial declarations of 'ChildWPFControl' must not specify different base classes
Now, I suppose I can write a ResourceDictionary XAML and define the front end there, but that becomes a problem if I want to add event handlers to the XAML (at least I couldn't find a way to do that)
Another alternative I have is to define the override template directly in the objects that use the ChildWPFControl but that makes the design less modular.
A final alternative I can think of is to make a xaml <-> xaml.cs pair that is a XAML style container and then force the ChildWPFControl to use the ControlTemplate defined within through the back end event handler.
Anyway, what I am looking for is an elegant and modular solution for my problem. Any advice would be welcomed.
Thanks
There are a couple of steps necessary to completely override a WPF Control. Some are necessary some are optional depending on your needs. I will explain the two important ones for you:
Creating a new default style
Every WPF control has somewhere a default style which contains it visual representation and override properties. Now if you derive from control WPF still thinks you want to use this default style, to change that you change the DefaultStyle in a static constructor like this
class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
Now if you use MyButton WPF tries to find a Style for MyButton, not for Button. OverridesDefaultStyle is a property in a style which might also be handy at some points. Usually these default styles should be placed in a theme related xaml.
Event Handlers when overriding classes
It is correct in a ControlTemplate or Style you can't use the syntactic sugar of using event like Click="OnClick". The point is, that the visual representation is decoupled from the logic part as much as possible. There are other ways though to overcome this, using the OnApplyTemplate method. By overriding this you ask the template "Give me this control" and then you just add your events there.
override OnApplyTemplate()
{
var innerChild = Template.FindName("PART_InnerChild", this) as MyInnerControl;
if(innerChild != null)
innerChild.SomeEvent += OnSomeEvent;
}
Note: The name of these controls usually begin with a PART_ by convention, this can be seen in WPF basic controls aswell. Its a nice way to tell the designers "Without this control, the logic part might break". There is also the attribute TemplatePart but it is not really important, WPF doesn't care about it. AFAIK Expression blend does some with it, personally i use it to tell other people what kind of inner controls are absolutely necessary to make this control work.
Personal advice
Deriving from a class is usually the last step we do when trying to customize controls. Because a lot of work is necessary to fully make it work and it can be limiting in reusability, we try to avoid it, for example a good alternatives are besides template overriding and styling; attached behaviors.
Lastly,
The whole subject is covered in a nice MSDN article.
Hope that helps
You can create your user control as wrapper, containing base control. In this way you can change styles in xaml add some logic in C# for wrapped contrŠ¾l. But it's tediously process.
Edit:adding sample(wrapper for telerik:RadComboBox )
XAML:
<UserControl x:Class="Controls.SingleDictionaryValueSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CardControls="clr-namespace:Controls"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" MinWidth="150" MinHeight="25" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="25"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- customize visual for wrapped control -->
<telerik:RadComboBox x:Name="cb"
Grid.Column="0"
VerticalAlignment="Center"
SelectedValuePath="Key"
ClearSelectionButtonContent="Clear"
ClearSelectionButtonVisibility="Visible"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
SelectAllTextEvent="None"
OpenDropDownOnFocus="false"
IsFilteringEnabled="True"
TextSearchMode="Contains"
EmptyText="Select item"
telerik:StyleManager.Theme="Metro"
FontFamily="Calibri"
FontSize="14"
IsEditable="True"
Foreground="#666"
KeyDown="cb_KeyDown"
SelectionChanged="cb_SelectionChanged"
GotMouseCapture="cb_GotMouseCapture"
DropDownOpened="cb_DropDownOpened"
KeyUp="cb_KeyUp">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadComboBox},Path=ActualWidth}" Text="{Binding Path=Value}" />
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
<CardControls:ErrorInfo x:Name="errorInfoControl" Grid.Column="1" Visibility="Hidden"></CardControls:ErrorInfo>
</Grid>
</UserControl>
CS:
public partial class SingleDictionaryValueSelector : IMyCustomInterface
{
....
private void cb_KeyDown(object sender, KeyEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_KeyUp(object sender, KeyEventArgs e)
{
SearchExecute();
}
private void cb_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_DropDownOpened(object sender, EventArgs e)
{
...
}
...
}
It looks like you have your inheritance mixed up more than that it is not allowed. Your root element of your xaml must match the base class of your xaml.cs.
If you are defining the base class in the same project, you will not be able to use it as the base class in the xaml, because it itself is still xaml and not a compiled control yet. Some ways to solve this: You can compile it in a seperate project and reference it, you can compile the base class entirely in .cs instead of a partial class, or you can use some style wizardry. Here is a link with examples of the last two: http://svetoslavsavov.blogspot.ca/2009/09/user-control-inheritance-in-wpf.html
What would be the point to using MVVM for the close action of File->Exit.
It seems like a lot of work to make a close command when you can simply create an event callback for the Click event. For something like this that doesn't have anything to do with data or business logic, I don't see the point to using MVVM approach. Why not just use the following:
xaml:
<MenuItem Header="_File" Background="WhiteSmoke">
<MenuItem Name ="Exit" Tag="Exit" Header="Exit" Click="Exit_Click"/>
</MenuItem>
Code behind:
private void Exit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
For that case, you have a bit of an argument. If nothing else, closing the view could easily be construed as entirely view related, so a Click event handler makes sense to start with. View-related code goes in the view, not the VM.
However, I would challenge your notion that a Command is that hard to set up. Using a DelegateCommand: http://wpftutorial.net/DelegateCommand.html requires two lines of additional code:
public ICommand ExitCommand {get; private set;}
public MyViewModel()
{
ExitCommand = new DelegateCommand(ExitApp);
}
The handler is the same either way. While Exit may not need a command, in general, ICommand is the right way to go and isn't actually that hard.
You're absolutely right - if handler is pure-UI related then it doesn't break MVVM by any means (despite what some extremists say). MVVM is pattern created to decouple UI and business logic - "UI things" in code are ok.
I'm having a WPF application which I can minimize to tray. When I normal-click it, the window shows again.
Now I'm wondering how to create a simple ContextMenu?
The ContextMenu has to get filled with x options which onclick will run a function. For now I just need an 'Exit'-item linked to an 'Exit_Click' method.
Something I've tried is:
ContextMenu menu = (ContextMenu)this.FindResource("NotifierContextMenu");
menu.IsOpen = true;
menu doesn't know of any IsOpen value.
Other examples like to use a lot of different things. One of them requires me to create a HostManager for some reason.
I just need a simple ContextMenu. How can I achieve this?
As #H.B. mentioned Hardcodet's NotifyIcon is pretty darn good for WPF Taskbar icons. Sucks you don't get that out of the box with WPF but you might as well use it and address your issue than wait for Microsoft to fix it(They really should just add that library into standards)
Now to solve your issue(Using above solution):
Download the solution
Build the library
Add it to your source control if you have one and add a reference to it(Hardcodet.Wpf.TaskbarNotification.dll) in your project
Now in your MainWindow.xaml you could just have something like:
<Window ...
xmlns:tb="http://www.hardcodet.net/taskbar"
...>
...
<Grid>
<tb:TaskbarIcon>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu>
<MenuItem Click="Exit_Click"
Header="Exit" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
...
</Grid>
</Window>
and MainWindow.xaml.cs with the click handler like you needed:
private void Exit_Click(object sender, RoutedEventArgs e) {
Application.Current.Shutdown();
}
I do recommend spending some time looking at the examples coming with the source code of the library to familiarize yourself with your available options. Trust me wpf has it way too easy when it comes to helper libraries. Try some of qt helper libraries and you'll know what "buried in there somewhere" literally means in opensource helpers.
Thus far, my testing has shown that all standard approaches, examples, and frameworks leveraging the MVVM pattern in silverlight suffer from a huge problem: massive memory leaks which prevent VMs from being garbage collected.
Obviously this is a huge and ridiculous claim - so my expectation is that someone will have an obvious answer of why and where I'm going wrong :)
The steps to reproduce are simple:
Bind your viewmodel to a view by setting the views datacontext to the VM (assume the viewmodel leverages INotifyPropertyChanged to support data binding)
Bind a UI element to a property on the viewmodel, for example:
<TextBox Text="{Binding SomeText}" />
Leverage the binding in some way (for example - just type in the textbox).
This creates a reference chain that extends from the root, to a BindingExpression, to your viewmodel. You can then remove the View from your UI tree, as well as all refernences to the VM - however the VM will never be garbage collected thanks to the root<>BindingExpression<>VM reference chain.
I have created two examples illustrating the problem. They have a button to create a new view/viewmodel (which should dump all references to the old one(s)) and a button which forces garbage collection and reports on the current memory usage.
Example 1 is a super stripped down caliburn micro example. Example 2 uses no frameworks and just illustrates the problem in the simplest way I could think of.
Example 1
Example 2
For those who might like to help but don't wish to download the example projects, here is the code for example 2. We start with a viewmodel called FooViewModel:
public class FooViewModel : INotifyPropertyChanged
{
string _fooText;
public string FooText
{
get { return _fooText; }
set
{
_fooText = value;
NotifyPropertyChanged("FooText");
}
}
private byte[] _data;
public FooViewModel()
{
_data = new byte[10485760]; //use up 10mb of memory
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
It simply exposes a string property called FooText which we will bind too. INotifyPropertyChanged is neccessary to facilitate the binding.
Then we have a view called FooView which is a usercontrol containing:
<UserControl x:Class="MVVMLeak.FooView">
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
<TextBlock Text="Bound textbox: " />
<TextBox Text="{Binding FooText}" Width="100"/>
</StackPanel>
</UserControl>
(namespaces omitted for brevity)
The important bit here is the textbox which is bound to the FooText property. Of course we need to set the datacontext, which I've chosen to do in the codebehind rather than introduce a ViewModelLocator:
public partial class FooView : UserControl
{
public FooView()
{
InitializeComponent();
this.DataContext = new FooViewModel();
}
}
MainPage looks like this:
<StackPanel x:Name="LayoutRoot" Background="White">
<Button Click="Button_Click" Content="Click for new FooView"/>
<Button Click="Button2_Click" Content="Click to garbage collect"/>
<ContentControl x:Name="myContent"></ContentControl>
</StackPanel>
with the following in the code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
myContent.Content = new FooView();
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Memory in use after collection: " + (GC.GetTotalMemory(true) / 1024 / 1024).ToString() + "MB");
}
Note: To replicate the problem, be sure to type something in the textbox, as I believe the Binding Expression isn't created until it's needed.
It's worth noting that this KB article may be related, however I'm not convinced since 'method 2' workaround doesn't seem to have an effect, and the reference chain doesn't seem to match.
Also, I'm not sure it matters, but I used CLR Profiler to diagnose the cause.
Update:
If anyone would like to test, and report their findings in a comment, I'm hosting the silverlight application via dropbox here: Hosted Example . To reproduce: Hit the top botton, type something, hit the top button, type something, hit the top button. Then hit the button. If it reports 10MB usage (or perhaps some other amount that is not increasing), you are not experiencing the memory leak.
Thus far, the problem seems to be happening on ALL of our development machines, which are ThinkPad w510 (43192RU) with 12GB Ram, 64 bit Win 7 Enterprise. This includes some which have not had development tools installed. It might be worth noting that they are running VMWare workstation.
The problem does NOT seem to happen on other machines I have tried - including several home PCs and other PCs in the office. We have somewhat ruled out SL versions, amount of memory, and probably vmware. Still haven't nailed down a cause.
A solution is yet to be found, however the problem is now identified. This behavior will occur if Silverlights' automation faculties are invoked due to:
Tablet PC Input Service (in other words, all 'tablet like' PCs)
Automated Testing tools
Screen Readers (and other accessability software)
More information here: http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx
So a new problem surfaces: How do we disable automationpeers or otherwise get them to clean-up correctly?
This post illustrates one approach: WPF UserControl Memory leak
However, it isn't really a viable solution as we'd have to override every silverlight control which we plan to use binding for, not to mention the control templates of complex controls.
I will change my answer if anyone can identify a good solution, but for now there doesn't seem to be one...
Edit:
Here is a nice little workaround which seems to do the job. Simply add the following parameter in your HTML where you define the silverlight object:
<param name="windowless" value="true" />
A side-effect of running in 'windowless' mode is that automation doesn't work :)
There is no memory leak in your second example.
After you affect a new FooView instance to your ContentControl using myContent.Content = new FooView();, there is no more used reference to the whole View + ViewModel object graph.
It'll be garbage-collected when needed, eventually.
Maybe you should clarify about what make you think there is a memory leak (i.e. statistics, repro steps...etc.)
I have defined my binding thus:
<TreeView
ItemsSource="{Binding UsersView.View}"
ItemTemplate="{StaticResource MyDataTemplate}"
/>
The CollectionViewSource is defined thus:
private ObservableCollection<UserData> users;
public CollectionViewSource UsersView{get;set;}
UsersView=new CollectionViewSource{Source=users};
UsersView.SortDescriptions.Add(
new SortDescription("IsLoggedOn",ListSortDirection.Descending);
UsersView.SortDescriptions.Add(
new SortDescription("Username",ListSortDirection.Ascending);
So far, so good, this works as expected: The view shows first the users that are logged on in alphabetical order, then the ones that are not.
However, the IsLoggedIn property of the UserData is updated every few seconds by a backgroundworker thread and then the code calls:
UsersView.View.Refresh();
on the UI thread.
Again this works as expected: users that log on are moved from the bottom of the view to the top and vice versa. However: Every time I call the Refresh method on the view the application hoards 3,5MB of extra memory, which is only released after application shutdown (or after an OutOfMemoryException...)
I did some research and below is a list of fixes that did NOT work:
The UserData class implements INotifyPropertyChanged
Changing the underlying users collection does not make any difference at all: any IENumerable<UserData> as a source for the CollectionViewSource causes the problem.
-Changing the ColletionViewSource to a List<UserData> (and refreshing the binding) or inheriting from ObservableCollection to get access to the underlying Items collection to sort that in place does not work.
I am out of ideas! Help?
EDIT:
I found it:
The Resource MyDataTemplate contains a Label that is bound to a UserData object to show one of its properties, the UserData objects being handed down by the TreeView's ItemsSource. The Label has a ContextMenu defined thus:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
The MyUserData object is a UserControl that shows All properties of the UserData object. In this way the user first only sees one piece of data of a user and on a right click sees all of it.
When I remove the MyUserData UserControl from the DataTemplate the memory leak disappears! How can I still implement the behaviour as specified above?
Step one in troubleshooting memory leaks is to find the definitive source. This is not always obvious and can occasionally defy your intuition. Based on your explanation, if you remove your user control, the issue disappears, but when you put it back, you start leaking again. This very likely points to a memory leak within that control (though not necessarily). Perhaps your control fits into one of the many types of WPF memory leaks, or you have a more classic problem of subscribing to an event but not properly unwiring it when no longer needed (the weak event pattern is useful here).
Your best bet is to grab a tool like .NET Memory Profiler or ANTS Memory Profiler (both are excellent and have free trials). Use one of these tools find the objects that are hanging around after they should be gone. These tools provide help in tracing the chain of objects that are hanging onto your object. There are many good articles on memory profiling on their sites, here on SO and on the wide open web.
You could try 2 things:
First of all the DropShadow has some problems, as it hold some references that should be garbadge collected.. see this article for more information:
http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/
You can try to set th HasDropShadow to false, and see what happens with your memory.
Secondly, if we have multiple ContextMenu objects of the same menu, you might want to create a ContextMenu resource in your Resources, and reference it with the StaticResource extension like so:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" x:Key="MyAwesomeContextMenu">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
And use it where you defined your context menu:
ContextMenu="{StaticResource MyAwesomeContextMenu}"
Hope this helps a bit!