Showing posts with label wpf. Show all posts
Showing posts with label wpf. Show all posts

Monday, April 25, 2011

Style Browser for WPF

Introduction

In this article I will show you simple application that can help you navigate in mass of your XAML styles. You can download sources from attachments to current article

My original article on codeproject, where you can download sources: http://www.codeproject.com/KB/WPF/stylebrowser.aspx

Background 

I am sure each WPF developer have met following problem: a lot of styles...a lot of images, brushes, different textboxes and textblocks. And sometimes it is hard to find resource key you need. This problem is often met in large WPF projects, with a lot of custom controls. When there are multiple developers - it can be much more hard! But for some cases - there is rather simple solution. Application described in this article can help you to make this process easier. Using StyleBrowser you can select XAML and look "inside" it. Application will show you all styles, images and brushes are stored in specified XAML file.
Also this application can be used when you want to use styles that you have created before and dony remember what exactly is inside XAML file. Just open XAML, find needed style and use it! 

Using the application

When you open application you see follwoing screen:

 Push "Select XAML" and open-dialog will appears on your desktop. Select XAML file with styles and press OK. If XAML is able to parse, then you will see something like this:

In the middle of application there is TabControl with number of tabs. Each tabs is a group of some styles from XAML. On screenshot tab "Buttons" is opened and you can see buttons from XAML.  "Sample content" - is added automatically to each control that has "Content" property. Reflection is used for detection.  To the right there is style Key and its classname. All styles are sorted A-Z.
Also, there is 2 usefull features, that can help you. You can select background, because some styles are white, some black...some black and white :) . Right now there is only 3 predefined colors. Black, white and gray. I think that it will be something like color picker in the future.
The second feature is searching. Just type any word in search textbox and you will see only styles that matches searched text. Color and search is show on following image

 Last interesing usefull feature is that you can see XAML. Just click on "XAML" button to the right of style you want to see. But this feature has one disadvantage at current step. It show you XAML with fully qualified classnames and also XML namespaces are added. But simple XAML's looks nice and can be usefull.


Code screenshots  

Lets make a short trip through code and take look ate how it works. At the beginning OpenDialog is used to selected file.
To parse XAML I simply use following code:

XamlReader reader = new XamlReader();
             FileStream stream = new FileStream(dlg.FileName, FileMode.Open);
             Uri uri = new Uri((new FileInfo(dlg.FileName)).Directory.FullName + "\\");
             object xaml = reader.LoadAsync(stream, new ParserContext() { BaseUri = uri });
 
We have to specify BaseUri here, so parser is able to process relative paths to images or any other files, if they exists in XAML. 
This code also has disadvantage. If some of tag cant be parsed, XAML will not be read at all. So here we got restrictions: XAML should contains only namespaces defined by default in .Net version sources was compilled in. My solution uses .Net 4.0.
Also, you should be carefull with images pathes. 
Next step is to fill resources of current window with loaded styles. We need it because there can be some styles that use another styles from that XAML. If we dont copy XAML to the resources -then referenced XAML will not loaded.

this.Resources.Clear();
                    if (xaml is ResourceDictionary)
                    {
                        foreach (DictionaryEntry item in (xaml as ResourceDictionary))
                        {
                            this.Resources.Add(item.Key, item.Value);
                            entries.Add(item);                           
                        }
                    }
Right now this code has another bad thing: sometimes some control of  main window that has style in resources will change it's look, even if  this control is not in preview area. I am going to fix it in future.
So, we have collection on styles, brushes and images, We can do everything with it. Sort, group, etc. In my app styles a sorted A-Z.
Next task is go through each item in collection and create preview in related tab control.

foreach (var item in list.OrderBy(e => e.Key.ToString()))
            {
                StyleItem styleItem = null;
                DictionaryEntry entry = (DictionaryEntry)item;
                if (entry.Value is Style)
                {
                    Style style = entry.Value as Style;
                    Type type = style.TargetType;
                    object obj = Activator.CreateInstance(type); 
if (obj is AnimationTimeline) continue;

                    styleItem = new StyleItem(obj as FrameworkElement, style, type, entry.Key.ToString());

                    if (type == typeof(Button) || type == typeof(ToggleButton))
                    {
                        buttonsPanel.Children.Add(styleItem);
                    } 
 
StyleItem is custom control that represent style preview. Each DictionaryEntry has Key and Value (as ususal has Dictionary :) ). Key is a x:Key attribute from XAML. Value is an content of XML-element.
We have to check what type  value is, because, for example you cant apply Style property if current item is an Image. Also, we need to skip any Animation, because seems it is impossible to create universal preview for animations. If item is brush or image I just assign background of preview as image or brush.
If item is style, there are 3 important lines, which extract style, object type and create instance of extracted type.

Style style = entry.Value as Style;
                    Type type = style.TargetType;
                    object obj = Activator.CreateInstance(type); 
 
Then StyleItem use this info to create related preview item in TabControl.
Another interesting thing is how preview XAML text is created.

string xaml;
                    try
                    {
                        xaml = XamlWriter.Save(entry.Value).Replace(">", ">\n").NormalizeXaml();
                    }
                    catch { xaml = "XAML cannot be parsed"; }
 
Why "Replace" method is used here? Because Save() return string without line breaks. So, I decided to insert line break after each ">".
Also, there is extension method for String:

NormalizeXaml() 
 
This method insert spaces at the beginning of each line depends on it deep in XML. I am not sure if I should show this method implementation in this article. 

Restrictions and future  

As for now, there is a number of issues in this application. But if you will find it usefull, please tell me about that. If developers will like what this app doing - I will work on it and add new usefull features and fix current issues.
In future I am going to:
- Skip styles with errors
- Add DLL's with resources
- Feature to add different XAML's. For example if one XAML has references to styles defined in another
- Add color picker to choose custom background color
- Clean XAML source viewer from namespaces and fullqualified pathes
- Create more smart object creator. For example: use predefined content template for menus, tabcontrols and other controls that has complicate content
- Make preview areas resizable. For example small text need small rectangle, but slider or image sometimes need huge area
- Maybe you will tell me abot any interestinf features


Sunday, April 3, 2011

Custom ShowDialog() method

In my first article I have considered DispatcherFrame. I have tried to look in-depth of DispatcherFrame, showed some IL sources of .Net and tried to explain what it is.
While reading MSDN about threading model, I met a section, which said that PushFrame() can be used to create something like WPF dialog windows. That windows take control after calling ShowDialog() and doesn’t return control to parent window until closed. So, I wondered, how to create such window?

Custom ShowModal()

For the first, I tried to open new window in separate thread. It works! But when I clicked on suspended parent window for 5-10 times, that window get “Not responded”. Not good!
I was really wondered how to block parent window, but prevent it from suspension. I thought about how to use DispatcherFrame here. Obviously, if main thread is blocked, then parent window will be also blocked, because of blocked dispatcher. So, there is no way to use PushFrame in such solution.
After that I decided to use Reflector to see how ShowDialog() is implemented in WPF. So, what I see? WINAPI used there to block each window in current thread.

<span lang="EN-US"></span>[SuppressUnmanagedCodeSecurity, 
SecurityCritical, DllImport("user32.dll", EntryPoint="EnableWindow", CharSet=CharSet.Auto)] 
public static extern bool IntEnableWindowNoThrow(HandleRef hWnd, bool enable); 
This method is called for each window with enable = false. As you guess solution becomes very closer. To implement my own ShowModal() I should perform following steps:
- Block parent window
- Add event handler on modal closed and unblock parent window inside this handler
- Show modal window and call PushFrame()
That’s it!

[DllImport("user32")]
        internal static extern bool EnableWindow(IntPtr hwnd, bool bEnable);

        public void ShowModal()
        {
            IntPtr handle = (new WindowInteropHelper 
(Application.Current.MainWindow)).Handle;
            EnableWindow(handle, false);

            DispatcherFrame frame = new DispatcherFrame();

            this.Closed += delegate
            {
                EnableWindow(handle, true);
                frame.Continue = false;
            };

            Show();
            Dispatcher.PushFrame(frame);
        }
 
First, we define extern method which uses WINAPI to enable/disable windows. Exactly that method used in WPF ShowDialog(). Than we blocks window, show modal and push new DispatcherFrame.
Why we have to use PushFrame() here? Answer is to prevent returning from ShowModal() method. When new frame is pushed, main loop suspended and new loop starts. This new loop processes system messages and execution point in main loop doesn’t go forward. When modal closed, DispatcherFrame.Continue = false, and this frame is exited. And after that application back to normal flow
This approach has no any obvious advantages in comparing with ShowDialog() method. Dialog behaviour is totally the same.

The one benefit, that is provided by this way is full control on blocking and unblocking windows from modal window. Say, what if u need to block only 2 of 4 active windows? Or unblock window while dialog is showed? It could be problematically with classic ShowDialog() method. Ofcourse, you can use WinAPI, but this is not so obviously as with custom ShowModal() method.

Also, you can put windows blocking and unblocking command anywhere. Just put PushFrame() and frame.Continue = false, to places of code, where you wanna stop or continue default application flow, and you will get it. It can be usefull if you want to continue main frame flow without closing of modal window or otherwise dont continue it after closing.

Thursday, January 27, 2011

DispatcherFrame. Look in-Depth


DispatcherFrame. Look in-Depth

I think that most all of you heard about DispatcherFrame. Let’s try to understand what is it and where it can be used? MSDN said: “Represent an execution loop in the Dispatcher”. Not enough info to understand and use it as I think. Also, there is example of very usefull procedure, but also, not obvious to understand. Here is it:
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;
    return null;
}

This procedure is DoEvents. As you can guess from it’s title – it force events to be performed. So, let’s  understand DispatcherFrame and how this method works.
Let’s start with Dispatcher class. I think everybody who works with WPF knows what it is. It is a class that works with UI thread in WPF. It contains queue of items that should be performed. I hope that everybody knows about Dispatcher and I don’t need to describe it in details.
To understand how Dispatcher works, I use ildasm.exe utility to disassemble WindowsBase assembly. I found Dispatcher class and look at its IL. There is method Run() which start Dispatcher work. Let’s look inside this method:

IL_0000:newobj instance void System.Windows.Threading.DispatcherFrame ::.ctor()
IL_0005:call void System.Windows.Threading.Dispatcher::PushFrame(class System.Windows.Threading.DispatcherFrame)

New DispatcherFrame is created and PushFrame() method is called. Let’s go to implementation of this method (I include here only important lines):

IL_0039:ldarg.0
IL_0043:call instance bool System.Windows.Threading.Dispatcher::GetMessage(valuetype System.Windows.Interop.MSG&, int, int32, int32)

IL_004d: call instance void System.Windows.Threading.Dispatcher::TranslateAndDispatchMessage(valuetype System.Windows.Interop.MSG&)

IL_0053: callvirt instance bool System.Windows.Threading.DispatcherFrame::get_Continue()
IL_0058:  brtrue.s IL_0039

In a friendly manner it looks like:

While (dispatcherFrame.Continue)
{
                Dispatcher.GetMessage();
                Dispatcher.TranslateAndDispatch();
}

So, while dispatcherFrame’s Continue property is true this cycle of reading system messages will be executed.
Let’s return to the MSDN definition: “Represent an execution loop in the Dispatcher”. Exactly this WHILE statement is represented by DispatcherFrame.
When application is started, Dispatcher.Run() is called. Dispatcher create default DispatcherFrame which represent main message loop in application. You can call Dispatcher.PushFrame(DispatcherFrame frame) to start new loop described above. Main loop will be “paused” until new DispatcherFrame.Continue = false.

Now, lets return to DoEvents() for WPF.  

[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;
    return null;
}

Let’s skip security attribute and go forward. Say, we have a very huge set of operations that should be performed in UI thread and while this application GUI should be updated. Since our operations are performed in UI thread, GUI will not be updated because Dispatcher will be busy by these operations.
How this problem can be solved?
In DoEvents() there is created new DispatcherFrame (frame variable) and one “Invoke” for dispatcher with Background priority, which set that frame shouldn’t be continued (frame.Continue=false). Background priority is lowest.
When you call DoEvents() there is set of items in Dispatcher that should be performed. Also, this set contains item, which should update our GUI and should be performed ASAP. When PushFrame() is called, main loop suspended and new loop starts (as described above). Dispatcher begin to get system messages AND to process items queue depending on it’s priority. Dispatcher will process all items and one of that items is operation that stop our new loop. It has lowest priority and will be performed last. After operation invoked, new loop stopped and flow returns to main loop and huge operations processing.