Show obsolete/deprecated class usages in XAML - c#

Context
We had quite a big memory leak in a WPF application we are developping that was caused by the use of DropShadowBitmapEffect in a resource library.
The drop shadow was used by some UserControl and by all our menus to cast a shadow over the actual content of the window, as such:
<DropShadowBitmapEffect x:Key="PopupDropShadow" ShadowDepth="1.5" Softness="0.15" />
...
<Rectangle BitmapEffect="{StaticResource PopupDropShadow}" ... />
I had to profile the application for many hours before I actually found the cause of the problem. The DropShadowBitmapEffect class is unmanaged and prevents objects from being GC'd. Also, you'll note that the DropShadowBitmapEffect class is marked as Obsolete, and that there is an updated class named DropShadowEffect that fixes the memory leak (and is also hardware accelerated thus improves render performance a whole lot). Here is the actual fix:
<DropShadowEffect x:Key="PopupDropShadow" ShadowDepth="1.5" />
...
<Rectangle Effect="{StaticResource PopupDropShadow}" ... />
Question
Is it possible to have the deprecated/obsolete class usages throw warnings on compile when used in XAML in Visual Studio 2010?

I haven't tested it for XAML but Gendarme has a rule that checks for the use of obsolete code.

Related

Better overview of visual WPF resources

Given: there are several resource dictionaries in WPF solution with various resources (vector graphics):
<ResourceDictionary ...>
<Canvas x:Key="SvgWarning" ... />
<Canvas x:Key="SvgError" ... />
<Canvas x:Key="SvgOk" ... />
...
<Path x:Key="PathBullet" ... />
<Path x:Key="PathMinus" ... />
...
Dictionaries are added to application resources. Those vector graphics are then used in different windows, user controls, etc.
Problem: over years of development they become too many. It's hard to tell how exactly which one looks like and I'd like to have better overview of them. E.g. if I'd have jpg-files referenced by a project then I could simply use windows explorer to preview how each looks like.
Currently I have to remember (or find) keys and then type it:
and only then I can see how it looks like. Silent intellisense is not a real problem (but a bonus points of course), because I simply want to preview images even before I decide to add one to a button.
Question: how can I organize it so I can quickly see which "witch" is which?
I can make a designer-only window (not used at run-time) and add all images there MANUALLY; by clicking each I can then see its key in xaml. Can I enumerate resources, detect which one is image (but not e.g. a style) and add it to such window? Maybe the way it's organized now is bad?
I don't need a concrete solution, rather a push in the right direction, hints from someone who has similar problem and have solved it.

XAML Forms not recognizing local and referenced classes

I opened a VS 2012 Solution in VS 2015 and cannot get it to compile. It was originally targeting .Net 3.5 and used the WPF Toolkit, which caused some ambiguous references. I resolved that by removing the reference to the WPF Toolkit and replacing it with the package DotNetProjects.Wpf.Toolkit.
Now I have to tackle the next issue, which I suspect is not caused by the obvious but is a cascade from some other hidden gotcha from upgrading the versions.
My XAML forms are not recognizing the existence of classes within referenced namespaces. I can get them to come up on intellisense but the application won't compile and the error says something like: "The name "MainWindow" does not exist in the namespace "clr-namespace:FieldSheetPrinter".
I have defined my main form with the following:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FieldSheetPrinter"
mc:Ignorable="d"
xmlns:Custom="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
xmlns:validationRules="clr-namespace:SWallTech.ValidationSupport;assembly=SWallTech.ValidationSupport"
xmlns:FieldSheetPrinter_Converters="clr-namespace:FieldSheetPrinter.Converters"
xmlns:Converters="clr-namespace:SWallTech.WPF.Support.Converters;assembly=SWallTech.WPF.Support"
x:Class="FieldSheetPrinter.MainWindow"
x:Name="MainWindow"
Title="CAMRA Field Sheet Printer - Stonewall Technologies, Inc."
Width="744"
Height="583"
WindowStartupLocation="CenterScreen"
Icon="CamraPrint.ico">
I have references to the SWallTech.WPF.Support project, which is in the solution, and the compiler has no problem recognizing the classes in that project's Converters namespace. This code has no error indicator. (Red squiggles.)
<Converters:IntGreaterThanZeroToVisibilityConverter
x:Key="IntGreaterThanZeroToVisibilityConverter" />
<Converters:DatabaseConnectionImageSourceConverter
x:Key="DatabaseConnectionImageSourceConverter" />
However if I try to refer to the ViewModel I am using as the datasource, I get an error:
<local:FieldSheetPrinterViewModel
x:Key="FieldSheetPrinterViewModelDataSource"
d:IsDataSource="True" />
I also get the error that the XAML form itself is not in the namespace FieldSheetPrinter, which of course it is, as you can see from the Code-Behind.
namespace FieldSheetPrinter
{
public partial class MainWindow : Window
...
}
I have tried everything I can think of, and I am truly stumped.
Joey
As suggested above, the problem wasn't directly with the XAML, or at least not completely. The original developer had named the Window tag with the same name as the code-behind class name, and that seems to be prohibited now. That was the only issue with the XAML. Everything else had to do with overlaps in the WPF and Windows.System.Data libraries that caused IValueConverter (in the System.Windows.Data namespace) to go unrecognized. I eliminated any other references that could have conflicted, and corrected some syntax issues in the code-behind that apparently were deprecated.
Ex.
stringVariable.IsNullOrEmpty()
was used instead of
string.IsNullOrEmpty(stringVariable)
In other places some ObservableCollection objects weren't recognized as enumerable, so I had to add "ToList()" before looping through the collection.
Weird, but I didn't write this stuff!
Thanks for the suggestions.
Joey

Problems with adding EnumMatchToBooleanConverter to my xaml file

I am trying to follow this radiobutton tutorial
I created a class called EnumMatchToBooleanConverter and it is in the top level of my wpf project. It says to place the inside a window.resources like this:
<Window.Resources>
<EnumMatchToBooleanConverter x:Key="enumConverter" />
</Window.Resources>
I am using it in a usercontrol so I have placed it inside a stackpanel instead:
<StackPanel.Resources>
<EnumMatchToBooleanConverter x:Key="enumConverter" />
</StackPanel.Resources>
I have Microsoft Visual Studio Ultimate 2012 and it gives me an error:
EnumMatchToBooleanConverter is not supported in a Windows Presentation Foundation (WPF) project.
Any ideas as to what I am doing wrong? Am I not allowed to place it inside a stackpanel.resources?
I just tried placing it inside a grid.resources
<Grid.Resources>
<EnumMatchToBooleanConverter x:Key="enumConverter" />
</Grid.Resources>
and it says
The type 'EnumMatchToBooleanConverter' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
Well it is in the same namespace as the rest of my project, so I'm unsure why it isn't finding it.
Change
<EnumMatchToBooleanConverter x:Key="enumConverter" />
for
<local:EnumMatchToBooleanConverter x:Key="enumConverter" />
All non-built-in classes you reference in XAML must be prefixed by their corresponding xmlns prefix.
HighCore's got it right. Just to add to this namespace discussion, I thought I'd point out another approach that can help make the code more readable or help you diagnose where certain Controls/Value Converters/etc are coming from (i.e., which assembly they are really coming from). This technique could allow your XAML to appear like Christian has it in his blog (without the xmlns prefix):
<EnumMatchToBooleanConverter x:Key="enumConverter" />
Essentially you perform some some namespace mappings to consolidate namespaces like this (only works if the files are in a different assembly/project). So in my example above you have mapped one of your namespaces to the default xmlns, so you would not need any prefix in the XAML.
I'm still trying to figure out how far to take this technique and Paul Stovell talks about taking it to the extreme, like I've shown above.
Even if you don't end up applying it to that degree, knowing about it might come in handy if you're looking at someone else's Xaml and they have applied a mapping like that. Knowing that would remind you to lookup the AssemblyInfo.cs file and check for the mapping and possibly help you track down where a Control/Value Converter/etc is actually located.

Memory leak on CollectionView.View.Refresh

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!

WPF Basics: Shared global styles for MVVM

I am attempting to use an MVVM-ish approach to my WPF development.
I have my logical view model classes under the ViewModel namespace, and I have matching styling for these view model classes under the View namespace.
For now I have my View information in ResourceDictionary XAML files, as DataTemplates and Styles, which are all merged into the single App.Resources ResourceDictionary in app.xaml.
However, I'm running into a sort of chicken/egg problem. I want there to be global styles that I use all over the place. For example, I want my own custom text style called MonkeyText which could be used in various stylings all over the place. I can't just set this in the app.xaml file, because the resourcedictionarys that will want to use MonkeyText are included by that app.xaml file.
I guess if that's impossible an alternative would be to use UserControls instead of mostly using DataTemplates to establish my views? I'm afraid that using UserControls would tie the VM and V parts too closely together.
WPF provides DynamicResources for just this reason. StaticResources - which most resemble 'traditional' references in programming - have just the problem you encountered; they need to be defined and loaded prior to the point that a style is parsed. DynamicResources, on the other hand, do not need to be defined prior to the point they are used - indeed, you can even create them on the fly. WPF takes care of ensuring that DynamicResources are automatically loaded by all of the styles that reference them once they are actually loaded.
Using DynamicResources is straightforward. When you create your MonkeyText style, create it as you normally would:
<Style TargetType="TextBlock" x:Key="MonkeyText">
<Setter Property="TextAlignment" Value="Center"/>
<!-- etc. -->
</Style>
And then refer to it from elsewhere using a DynamicResource:
<TextBlock Text="Hello, World!" Style="{DynamicResource MonkeyText}"/>
If, for any reason, WPF cannot resolve your DynamicResource, it will fail silently without any exception thrown (StaticResources do throw exceptions when the cannot be resolved). It will, however, print a debug message when this happens - so keep an eye on the Output window in Visual Studio.
Since DynamicResources work with resources that are loaded at any point in any order, you can structure your resource dictionaries any way you like - so putting them in with your other View styles and merging them in via the single App.Resources ResourceDictionary in app.xaml will work fine.
More details on DynamicResources can be found in the MSDN docs for WPF.

Categories

Resources