Archive

Archive for the ‘WPF’ Category

Textblock showing full text in tooltip when text truncated inside control

June 3, 2012 1 comment

This post describes implementation of custom textblock which will show the full text of the textblock in tooltip when Text can not be shown fully inside the given width of textblock control.

Textblock has property named “TextTrimming”. Value set for this property defines how text is trimmed when it overflows from the edge of Textblock. When “TextTrimming” property is set to “WordEllipsis’ / “CharacterEllipsis” and text value can not be shown fully in given width then text will truncate with “…” (i.e. ellipsis) shown at the end of text. Now one option to show the full text to user is through Tooltip. This can easily be achieved by binding text value as tooltip. However, the heck is showing full text value as tooltip only when needed i.e. when text has been truncated inside Textblock.

Unfortunately, Textblock doesn’t provide any property to indicate that text has been truncated / trimmed. Hence, we need to detect when the text has been truncated i.e. text size has increased beyond the width of textblock’s width. So now the problem is measuring width of text considering text settings applied. “FormattedText” class provided by Microsoft can help us for this. “FormattedText” takes into consideration FontSize, FontStyle, FontWeight, FlowDirection, current culture, etc. while calculating the width of text. Following code snippet shows how to measure text width using “FormattedText”.

Typeface typeface = new Typeface(this.FontFamily,
                this.FontStyle,
                this.FontWeight,
                this.FontStretch);            

// FormattedText is used to measure the whole width of the text held up by this container.
FormattedText formmatedText = new FormattedText(
                this.Text,
                System.Threading.Thread.CurrentThread.CurrentCulture,
                this.FlowDirection,
                typeface,
                this.FontSize,
                this.Foreground);

After getting the FormattedText object, just Check

formmatedText.Width > this.ActualWidth

to determine whether text has been truncated or not.

I have attached herewith the full implementation of Custom TextBox class (TextBoxEx) in a word file. In the implementation when Tooltip is opening, it is checking whether text has truncated or not. If text has not truncated then tooltip won’t be displayed (as it is not necessary because full text is anyway visible). Also, in order to show the tooltip, text property of the Textblock should be bound with the tooltip.

ToolTip="{Binding Path=Text, RelativeSource={RelativeSource self}}"

It is pretty obvious that if binding for tooltip is not set then this control will not work.

This one is pretty simple fix for the problem and it works well in the scenarios I have tested so far. Any feedback/bug/enhancement request on this is welcome.

TextBlockEx

Subclassing WPF user control

September 21, 2011 3 comments

It is not very common to do User Control subclassing but if you have some compelling reason to do so then following detail explains how to do it with WPF User Control. Due to xaml, user control subclassing in WPF becomes little tricky. Following steps explain the procedure :

1. Create a base control as simple c# class and not WPF user control. Derive this base class from UserControl.

    public class ESQBaseControl : UserControl
    {
		#region Constructor
		
        protected ESQBaseControl()
        {
            // protected constructor is required so that derived controls can have default parameterless constructor which is needed for xaml 
            // also protected constructor will ensure that new control creation always use constructor by passing parameters (e.g. in this example passing of string parameter is must in order to construct control)
        }

        public ESQBaseControl(string info)
        {
            
        }
		
		#endregion
		
		#region base implementation
		
		public virtual void SomeOperation()
		{
		
		}

		#endregion
	}

2. Base class will not have UI implementation because of the absence of xaml. So time to build some UI for our control in concrete control xaml.

	
	<view:ESQBaseControl x:Class="<namespace>.ESQConcreteControl"
             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:view="clr-namespace:<namespace in which base control resides>"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
		<Grid>
			
			<Grid.RowDefinitions>
				<RowDefinition/>
				<RowDefinition/>
			</Grid.RowDefinitions>

			<Label Content="My Control"/>
			<TextBox x:Name="MyTextBox" Grid.Row="1" Text="Some Text"/>
		</Grid>
	</view:ESQBaseControl>

In above code note that whole xaml has been wrapped inside BaseControl class while class name inside xaml definition is given of the concrete class.

3. Finally, inside the code behind file user control’s concrete logic can be implemented :

	public partial class ESQConcreteControl : ESQBaseControl
    {
        #region Constructor

        public ESQConcreteControl(string info) : base(info)
        {
            InitializeComponent();
        }
        
        #endregion
		
		#region Control implementation
		
		public override void SomeOperation()
		{
			myTextBox.Text = "Hello World!!";
		}
		
		#endregion
	
	}
	

Hope this helps!!

Interop & IDataErrorInfo

September 17, 2011 Leave a comment

Despite of all the interop support available sometimes you will still come across some tricky issues which will be difficult to overcome. One such problem I faced recently. I had to configure the Dialog with MFC PropertySheet control. This PropertySheet control was holding few Property pages. These property pages didn’t had any MFC controls as content but hosting the user controls developed in WPF. First I will write very briefly about how to load the WPF user control inside the MFC container and then I will explain about my tricky problem and how I fixed it.

Hosting of WPF user control as content of PropertyPage wasn’t that challenging. In order to host the WPF content inside property page, first HwndSource needs to be configured. In order to create HwndSource, first you need to create HwndSourceParameter. Typical configuration for HwndSourceParameter can be as under :

System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters (<Window Name>);

// X position where WPF content to be placed
sourceParams->PositionX = x;
// Y position where WPF content to be placed
sourceParams->PositionY = y;
// height of the WPF content
sourceParams->Height = height;
// width of the WPF content
sourceParams->Width = width;
// Handle for the Parent window inside which WPF content to be hosted
sourceParams->ParentWindow = IntPtr(parent);
// style attributes as needed (“WS_TABSTOP” style needs to be set if WPF content needs to get focus while tabbing
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP;

Another important property for HwndSourceParams is “ResetFocusMode”. Default value for this property is “Auto”. Setting this property to “Auto” makes sure that when when the PropertySheet control (in my case) looses the focus and receives focus back the control which had focus prior to loosing focus retains the focus back. If you set this property to “None” then, none of the controls inside the page will have focus when propertysheet control receives the focus.

// now create HwndSource using this parameters
System::Windows::Interop::HwndSource^ hwndSource = gcnew HwndSource(*sourceParams)

Now suppose I have a WPF user control named “PageUserControl” then create instance of this user control.

PageContentUserControl^ pageContent = gcnew PageContentUserControl();
//Now set this user control as RootVisual of HwndSource object. 
hwndSource->RootVisual = pageContent ;

With above code in place it is possible to host the WPF content inside MFC control. This is just basic information which one should know in order to host the WPF content inside MFC container however somewhat plumbing work will be needed depending upon MFC container control in which you are placing the WPF control.

After hosting the control I had one more issue to deal with. My User control developed in WPF had few checkboxes, text boxes, etc. as it’s content. In order to validate my inputs and giving visual indication about the data validation failure, I had implemented interface “IDataErrorInfo”. (Now how this interface is implemented and how it works is not covered in this Post. Google it to find more on it)

When “IDataErrorInfo” interface implemented and bound value to the control is invalid, control will have a red border around it to indicate user about the validation failure. Also, when you set focus on the control like check box through tabbing, you will not be able to see the dotted line around check box when it receives the focus. Following figure shows the focus styles for each case. When User Control is hosted within MFC container, you will assume that “IDataErrorInfo” implementation should work but infect it won’t.

The problem of not being able to see all such decoration is absence of Adorner layer. All these error templates and focus styles etc. are drawn inside the AdornerLayer. The WPF window has an AdornerLayer by default, so we can always see validation errors in WPF. When hosting a user control in MFC, adornerlayer is not available by default and need to add an AdornerLayer into your UserControl to support adorners.

So in order to view all such Focus styles / Error template styles, we need to decorate the UserControl with AdornerDecorator. So following code should do the fix for you :

<UserControl x:Class="TestPjt.TestUC"
             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:TestPjt"
             mc:Ignorable="d" 
             d:DesignHeight="50" d:DesignWidth="300">
    <AdornerDecorator>
        <Grid>
              <TextBox x:Name="txtbox"/>
        </Grid>
    </AdornerDecorator>
</UserControl>

This fix will start showing the expected focus and error template style for each control. HTH!!

UpdateSourceTrigger – DataBinding

August 22, 2009 6 comments

Last week while working on two way binding, I discovered one very important settings for binding i.e. “UpdateSourceTrigger”. Before discovering this feature I faced problem in setting the value from target to source. Let me describe the problem I faced.

In my setup, I had no. of text boxes in window whose text property were bound to the property of object in collection. So in this case, source for me was property of the object in collection and target was the text property of textboxes.

I configured my binding object as below :

            Binding binding = new Binding();
            binding.Source = <source object from collection>;
            binding.Path = new PropertyPath
(<PropertyName>);
            binding.Mode = BindingMode.TwoWay;
            txtbox.SetBinding(TextBox.TextProperty, binding);

With above binding configuration, source –> target binding worked perfectly for me. But binding from target –> source (i.e. from text box to object’s property in collection) was not working for me. One thing which is important to mention here is, I was changing the text box’s text value in procedural code and not by entering the value in textbox.

It was beyond my understanding why binding was not working? I spent almost half a day trying to figure out this. I googled it but google disappointed me. So I took Adam Nathan’s “WPF Unleashed” book in my hand and started looking for some solution. Again this book came to my rescue. While going through the “Data Binding” chapter in this book, I learned about “UpdateSourceTrigger’ property of the binding object.

“UpdatesourceTrigger” property is very important in “TwoWayBinding” and “OneWayToSource” binding modes. It defines how and when source should get updated.

Value of UpdateSourceTrigger could be any of the value from UpdateSourceTrigger Enumeration. Enumeration values are

  1. PropertyChanged – The source is updated whenever the target property value
    changes.
  2. LostFocus – The source is updated when target property changes and target object looses focus.
  3. Explicit – The source is updated when explicit call is made to update using “BindingExpression.UpdateSource”.

Target controls have default values set for “UpdateSourceTrigger” for binding object. For text box control, default setting for this property is “LostFocus”. In my case, I was updating the textbox (i.e. target) through code and because of that textbox was not loosing focus. Hence my binding was not working. Then I changed above binding configuration to set the UpdateSourceTrigger property of binding object as following :

            Binding binding = new Binding();
            binding.Source = writeItemInfoList[index];
            binding.Path = new PropertyPath("ItemValue");
            binding.Mode = BindingMode.TwoWay;
            binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            txtbox.SetBinding(TextBox.TextProperty, binding);

And Binding from Target to source started working for me.

Resources in WPF – II (Logical Resources)

February 17, 2009 3 comments

Logical resources are arbitrary .net objects stored (and named) in element’s ResourceDictionary. Logical resource can be shared among multiple elements depending upon the scope at which it has been defined. Both FrameworkElement and FrameworkContentElement has ResourceDictionary defined in it, hence almost all UI elements inherit the ResourceDictionary. Typical logical resources are styles, bindings, etc.

Logical resources are of two types :

1) StaticResources
2) DynamicResources

How resources are searched?

StaticResource/DynamicResource markup extension accepts a key name as parameter. So this key is searched while looking up for resource. It is not mandatory that resource has to be defined on the element which is using it. Xaml looks for the resource at the element level first then to parent level and then to application level. Resource markup extension implements the ability to walk the logical tree to find the items upward in the logical tree so if resource is not found, it checks the parent element, its parent and so on until it reaches the root element. At this point, it checks the resources collection at the application level and system level subsequently. If the resource is not found anywhere, it throws “InvalidOperationException”.

Although it is advisable to have unique key associated with resource, however it is not mandatory. While looking up for the resource whatever  resource is found first is assigned.

Static v/s. Dynamic Resources

Static Resources

Dynamic Resources

StaticResource is applied only once (first time when it is used).

DynamicResource is applied every time when resource changes e.g. system colors can be changed using control panel. So if system colors has been used as resource, than update is required. DynamicResource will take care of such change in resource.

StaticResource has lesser overhead as it is applied only once during the runtime of the application.

DynamicResource has more overhead as it is applied whenever the resource value changes. In order to do this, DynamicResource has to keep tracking about the changes in the resource during runtime also.

StaticResources always get loaded the first time window/page is loaded irrespective of its use during start up.

DynamicResources are not loaded until it is actually used. So this results in better load time.

StaticResources can be used for DependencyProperty as well as normal .net property.

DynamicResource can only be used for DependencyProperty.

StaticResources has to be defined before it is used. Hence, StaticResources can not be forward referenced. DynamicResources can use the forward references.

Defining and Applying Resources from procedural code

At element level adding resources (at definition level, static/dynamic resource is not differentiated) is easy. The syntax is

<element>.Resources.Add(object key, object value);

Static Resource

If in xaml, resources are used as static resource as shown below (provided resource is already defined)

<Button BackGround={StaticResource backgraoundBrush}/>

then from procedural code, this resource can be used as

Button button = new Button();
button.BackGround = (Brush)button.FindResource(“backgroundBrush”);

If “FindResource” fails to find the resource, it throws  “ResourceReferenceKeyNotfoundException”. To avoid this situation, “TryFindResoruce” method can be used which returns ‘null’ if the resource key is not found.

Dynamic Resource

in xaml, dynamic resource can be used as mentioned below :

<Button BackGround={DynamicResource backgraoundBrush}/>

however in procedural code this can be set using SetResourceReference method as shown below :

button.SetResourceReference(Button.BackgroundProperty, “backgroundbrush”);

Do remember that forward reference (i.e. defining the resource before using it) still remains invalid for Static Resource even with procedural code.

Accessing Resources from other assemblies

In many cases resources are defined in separate assembly for the sake of better maintainability. In that case resources can be accessed from different assembly in following way ;

<Button BackGround={DynamicResource {ComponentResourcekey TypeinTargetAssembly=”local:MyResources, ResourceID=backgroundBrush }}

Sometimes the other assembly in which the resource has been defined provides way to access the resource by defining property in class.

public object MyResourceKey

{
      get {return (new ComponentResourcekey (this.GetType(), “backgroundBrush”);}    

}

In such a case, resource can be accessed from xaml in following way :

<Button BackGround=”{DynamicResource {x:static local:MyResourceKey}}”

Accessing System Resources

Sysem Resources i.e. System Fonts, System colors etc. can be used in similar way as mentioned above.

<Button BackGround =”{DynamicResource {x:static SystemColors.Desktopcolor}}”

Categories: Resources, WPF

Resources in WPF – I (Binary Resources)

February 16, 2009 12 comments

There are two types of resources in WPF :

1) Binary Resources
2) Logical Resources

In this post, I have described the details of the Binary resources.

Binary Resources : Binary resources could be logo/image files, AV files etc. Resource files which are added to project, can have the “Build Action” defined on it from the file properties windows.

Out of the various build actions defined by WPF only following two are important for binary resources :

image

  • Resource : Embeds resource into the assembly (or culture specific satellite assembly)
  • Content : this leaves resource as loose file and upon compilation this resource information is not embedded to assembly. Instead, it adds custom attribute to the assembly (AssemblyAssociatedContentFile) which records the existence and relative location of file.It is also possible to access the resource file without adding into the project however with this approach management of resource file becomes bit difficult. However, this approach is useful is resource file is generated dynamically using some runtime information. In such a case, resource file will not be available at compile time so can not be added to project.

What is the difference between the “Resource” and “Embedded Resource” build actions?
“Resource” and “Content” build actions are to access the WPF resources using the Uris. However “Embedded Resource” is for prior technologies. However both options embeds the resource in assembly but “Resource” option to be used for WPF.

URIs for accessing the binary resources

URI

Resource

“Logo.jpg”
  • embedded in current assembly (using “Resource” as build action)
  • loose resource however physical file is placed along with xaml file or assembly (if selected build action is “Content”)
“A/B/Logo.jpg” same as above but resource file is located in subfolder “A/B”
“D:\Resource\Logo.jpg” resource path hard coded
http://pinvoke.net/logo.jpg loose resource hosted at pinvoke.net website.
ResourceDll;component/Logo.jpg this URI represents the resource embedded in assembly ResourceDLL.dll or ResourceDLL.exe
ResourceDll;componen/tA/B/Logo.jpg same as above except resource is located in subfolder
pack://siteOfOrigin:,,,/logo.jpg resource located at site of origin
pack://siteOfOrigin:,,,/A/B/logo.jpg resource located at site of origin in sub folder

Site of Origin :

Site of origin at the runtime gets resolved in different ways depending upon the way in which the application has been deployed.

  • For a full-trust application installed with Windows Installer, the site of origin is the application’s root folder.
  • For a full-trust ClickOnce application, the site of origin is the URL or Universal Naming Convention (UNC) path from which the application was deployed.
  • For a partial-trust XAML Browser Application (XBAP) or ClickOnce application, the site of origin is the URL or UNC path that hosts the application.
  • For loose XAML pages viewed in Internet Explorer, there is no site of origin. Attempting to use it throws an exception.

Accessing resources from procedural code :

While accessing the resource from procedural code in WPF, triple comma syntax is used however above table for URIs remains still applicable except the last two rows with site of origin.
e.g. to read the embedded resource with “Resource” action “pack://application:,,,/logo.jpg’” can be used.

Following code shows how to read the resource from procedural code :

Image image = new Image();
image.Source = new BitmapImage(new Uri(“pack://application:,,,/logo.jpg”))

There is one more aspect to binary resource and that is localization but I’m not covering it as of now.

retrieving command-line args for WPF application

February 12, 2009 Leave a comment

Normally in winform applications, command line arguments are accessed from the string array argument in the main method. However in WPF main method is auto generated and can not be acceEditssed directly. So how to access the command line args with WPF application.

The answer is use “System.Environment.GetCommandLineArgs”. Calling this method returns command line arguments at any given point of time.

Categories: Startup, WPF