Silverlight: a generic Pagination Control

Print Content | More

The default silverlight controls like DataGrid and ItemsControl come without a default paging mechanism like we have in ASP.NET, so this article will be forcused in developing a generic Pager Control in Silverlight. This is what we want to obtain:

SilverlightPagination1

I was lurking around looking for some informations on ASP.NET MVC, and I found two interesting articles on how to do pagination in ASP.NET MVC (http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/ and http://code-inside.de/blog-in/2008/04/08/aspnet-mvc-pagination-view-user-control/), the tecnique seemed inetersting and I decided to use it in my pager control.

The control will be divided in 2 major areas, the pager itself a series of helper classes that will give us different views of the data (a single 'page' of data can be tought as a view) and the selection control (which will use the pager) that will display a series of buttons to choose which page of data to display.

Let's start with the pager, since the control do not have to know the exact type of the data for which he will provide the paging features we need to define two interfaces: one that represent the view of the data we want to display (the actual page displayed) and another that will be the functions exposed by the pager itself.

Here we have some snippets of code taken from the two interfaces:

/// <summary> /// Generic functions exposed by the view, the actual implementation /// of this interface will be based on a list class to hold also the /// data of the view. /// </summary> public interface IPagedList : System.Collections.IList { /// <summary> /// total number of records /// </summary> int TotalCount { get; } /// <summary> /// index of the page represented by this view /// </summary> int PageIndex { get; } /// <summary> /// Size of the page /// </summary> int PageSize { get; } ...

/// <summary> /// do a previous page exists? /// </summary> bool IsPreviousPage { get; } /// <summary> /// do a next page exist? /// </summary> bool IsNextPage { get; } ... } /// <summary> /// functions exposed by the pager, they will build a view over the data /// </summary> public interface IPager { IPagedList ToPageList(int index); IPagedList ToPageList(int index, int pagesize); }

 

We need this level of abstraction cause we provide a strongly type implementation of the classes using Generics. All the paging job is done in the constructors of the view, here we take advantage of the current implementation of List<T> and IQuerable<T>:

/// <summary> /// The physical implementatio of the view, it's based on a List<> to also hold the data /// the source can be a List of anything or the result of a Linq query /// </summary> /// <typeparam name="T"></typeparam> public class PagedList<T> : List<T>, IPagedList { public PagedList(IQueryable<T> source, int index, int pageSize) { this._TotalCount = source.Count(); this._PageSize = pageSize; this._PageIndex = index; this.AddRange(source.Skip(index * pageSize).Take(pageSize).ToList()); } public PagedList(List<T> source, int index, int pageSize) { this._TotalCount = source.Count(); this._PageSize = pageSize; this._PageIndex = index; this.AddRange(source.Skip(index * pageSize).Take(pageSize).ToList()); } ...

public int TotalCount { get { return _TotalCount; } } private int _TotalCount;

 

and :

/// <summary> /// extremely simple implementation of the pager, /// given a list of data or a Linq query /// this class will build a series of views over it /// </summary> /// <typeparam name="T"></typeparam> public class Pager<T> : IPager { public Pager(List<T> source) { lSource = source; } private List<T> lSource; public Pager(IQueryable<T> source) { qSource = source; } private IQueryable<T> qSource; private const int DefaultPageSize = 10; public IPagedList ToPageList(int index) { if (lSource != null) return new PagedList<T>(lSource, index, DefaultPageSize); return new PagedList<T>(qSource, index, DefaultPageSize); } public IPagedList ToPageList(int index, int pagesize) { if (pagesize <= 0) pagesize = DefaultPageSize; if (lSource != null) return new PagedList<T>(lSource, index, pagesize); return new PagedList<T>(qSource, index, pagesize); } }

 

That's all for our set of helpers classes, on to the Control itself now; This first implementation will be extremely simple, The basic idea is to use a StackPanel as the main container and to dynamically add buttons to it, we will have 1 button for each page, plus button for previous, next, first and last page selection.

This control will act as a DataSource (or ItemsSource) for other controls able to render lists of data, so we need a way to specify to which control it will be linked, for the purpose we define a 'LinkedControl' property which will be setted to the name of the control we want to use to display the paginated data. The control has to support the 'ItemsSource' property so we need to do a check:

... /// <summary> /// Gets or sets the LinkedControl possible Value of the Control object. /// </summary> public Control LinkedControl { get { return (Control)GetValue(LinkedControlProperty); } set { //check if the control supports the items source property _itemsSource = value.GetType().GetProperty("ItemsSource"); if (_itemsSource == null) throw new ArgumentException("ItemsSource not supported"); SetValue(LinkedControlProperty, value); } } /// <summary> /// Identifies the LinkedControl dependency property. /// </summary> public static readonly DependencyProperty LinkedControlProperty = DependencyProperty.Register( "LinkedControl", typeof(Control), typeof(SilverlightPager), new PropertyMetadata(null)); private PropertyInfo _itemsSource; ...

 

The control will also be able to support the use of templates to define the graphical representations of the buttons, so we'll have two dependancy properties (PagerButtonStyle and PagerSelectedButtonStyle) to set the staticresources to use for the buttons.

... /// <summary> /// Gets or sets the PagerButtonStyle possible Value of the Style object. /// </summary> public Style PagerButtonStyle { get { return (Style)GetValue(PagerButtonStyleProperty); } set { SetValue(PagerButtonStyleProperty, value); } } // Using a DependencyProperty as the backing store for PagerButtonStyle. This enables animation, styling, binding, etc... public static readonly DependencyProperty PagerButtonStyleProperty = DependencyProperty.Register("PagerButtonStyle", typeof(Style), typeof(SilverlightPager), new PropertyMetadata(null)); /// <summary> /// Gets or sets the PagerSelectedButtonStyle possible Value of the Style object. /// </summary> public Style PagerSelectedButtonStyle { get { return (Style)GetValue(PagerSelectedButtonStyleProperty); } set { SetValue(PagerSelectedButtonStyleProperty, value); } } ...

 

The main core function actually gets the data for the view to visualize and recreate the whole sets of buttons everytime you select a new page:

/// <summary> /// buildup the pager aligning the buttons /// </summary> private void GetDataAndBuildPager() { if (_Source == null) throw new ArgumentException("No pager setted"); //Get the page data IPagedList page = _Source.ToPageList(_CurrentPageIndex, PageSize); //Build the layout LayoutRoot.Children.Clear(); //First button Button b = new Button(); b.Tag = 0; b.IsEnabled = !page.IsFirst; b.Content = "<<"; b.Click += new RoutedEventHandler(b_Click); SetButtonStyle(b, PagerButtonStyle); LayoutRoot.Children.Add(b); //Prev button b = new Button(); b.Tag = _CurrentPageIndex - 1; b.IsEnabled = page.IsPreviousPage; b.Content = "<"; b.Click += new RoutedEventHandler(b_Click); SetButtonStyle(b, PagerButtonStyle); LayoutRoot.Children.Add(b); for (int i = Math.Max(0, _CurrentPageIndex - PageInterval); i <= Math.Min(page.TotalPages, _CurrentPageIndex + PageInterval); i++) { b = new Button(); b.Tag = i; b.Content = i.ToString(); b.IsEnabled = true; LayoutRoot.Children.Add(b); if ((i == page.PageIndex) && (PagerSelectedButtonStyle != null)) SetButtonStyle(b, PagerSelectedButtonStyle); else { b.Click += new RoutedEventHandler(b_Click); SetButtonStyle(b, PagerButtonStyle); } } //Next Button b = new Button(); b.Tag = _CurrentPageIndex + 1; b.IsEnabled = page.IsNextPage; b.Content = ">"; b.Click += new RoutedEventHandler(b_Click); SetButtonStyle(b, PagerButtonStyle); LayoutRoot.Children.Add(b); //Last Button b = new Button(); b.Tag = page.TotalPages; b.IsEnabled = !page.IsLast; b.Content = ">>"; b.Click += new RoutedEventHandler(b_Click); SetButtonStyle(b, PagerButtonStyle); LayoutRoot.Children.Add(b); //update the linked control SetItemsSource(page); } void b_Click(object sender, RoutedEventArgs e) { //set the new current page _CurrentPageIndex = (int)(((Button)sender).Tag); GetDataAndBuildPager(); }

 

This is only a first release and the control itself is far from beeing optimized, it can be a starting point for deriving your own Pager control, The test solution accluded shows an example of paginating a Datagrid and an ItemsControl.

Example Solution:



Control, Silverlight, Window, Pagination, Datagrid, Itemscontrol

8 comments

GMail: Account Suspended = Account Forever Lost

Print Content | More

In the first days of August I have created a brand new GMail account to use some of thervices Google provided...15 to 20 days later I had to access that account via web interface...unfortunately happened that I had forgot the password and after some attempt the system blocked my account.

I tried every so-called password recovery procedure that google has, tried to fill every form they provide with all the in formation I could remind and this was their automated reply:

"Thank you for your report. The account in question is disabled, and we
can't provide you with access to it. For more information about disabled
accounts, please visit the following link in the Google Accounts Help
Center: ... etc etc..."

which basically brought me to the same password recovery shit tried before.

I tried to look in their user assistance forum and saw that many many people have the same problem...after an account block it seems there's no way to resume it, writing to the assistance center is pointless..they don't reply anyway.

As you can immagine I'm a bit disappointed by the low quality of the service provided by google with GMail, I have many free emails account made with different companies that offers similar services and it never happened something like this.



8 comments

Silverlight: Custom Buttons with Templates

Print Content | More

Sometimes you want to define a consistent look for all you application and maybe you completely replace the template of a control (lets say...a button :D), then you want to use the same template with small variations (colors or images for example).

Lets say we have realized the usual round button with a gradient background and that you want to use different colors which have different meanings for the interface, the same principles apply to any other control or custom controls.

Actually you can do it all just defining styles in the App.xaml file, like this:

<RadialGradientBrush x:Name="RedRadialBrush" GradientOrigin="0.2,0.2" > <GradientStop Color="#FFFFFFFF"/> <GradientStop Color="Red" Offset="1"/> </RadialGradientBrush> <Style x:Key="RoundTextButton" TargetType="Button"> <Setter Property="Background" Value="{StaticResource GrayRadialBrush}" /> <Setter Property="Width" Value="30" /> <Setter Property="Height" Value="30" /> <Setter Property="FontSize" Value="10" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> <vsm:VisualStateManager.VisualStateGroups> <vsm:VisualStateGroup x:Name="FocusStates"> <vsm:VisualState x:Name="Unfocused"/> ... </Style>

and you can instantiate some on a page like this:

<Grid x:Name="LayoutRoot" Background="White"> <StackPanel> <StackPanel Orientation="Horizontal"> <Button Content="Edit1" Style="{StaticResource RoundTextButton}" /> <Button Content="Edit2" Style="{StaticResource RoundTextButton}" Background="{StaticResource YellowRadialBrush}" /> <Button Content="Edit3" Style="{StaticResource RoundTextButton}" Background="{StaticResource GreenRadialBrush}" /> <Button Content="Edit4" Style="{StaticResource RoundTextButton}" Background="{StaticResource RedRadialBrush}" /> </StackPanel> ... </StackPanel> </Grid>

 

The code above produce this:

CustomButtons1

4 small round buttons with animations when your mause move over them or when you press them.

What we see here is that the xaml code of the page looks a little bloated and not very well readable, plus changing a single resource as the background color of the buttons in code is a little messy cause you have to create a new brush consistent with the previous ones.

What we can do is define our own Button control (which inherits from Button), define the properties we want to access directly (as dependancy properties to support binding and templating) and then move the master template in the Generic.xaml file. The button class is straightforward and doesn't need many comments:

public class CustomButton : Button, INotifyPropertyChanged { public CustomButton() : base() { this.DefaultStyleKey = typeof(CustomButton); } public override void OnApplyTemplate() { base.OnApplyTemplate(); GradientStop gs = GetTemplateChild("EllipseBackGroundColor") as GradientStop; if (gs != null) gs.Color = BackgroundColor; } #region BackgroundColor /// <summary> /// Gets or sets the BackgroundColor possible Value of the Color object. /// </summary> public Color BackgroundColor { get { return (Color)GetValue(BackgroundColorProperty); } set { SetValue(BackgroundColorProperty, value); } } /// <summary> /// Identifies the BackgroundColor dependency property. /// </summary> public static readonly DependencyProperty BackgroundColorProperty = DependencyProperty.Register( "BackgroundColor", typeof(Color), typeof(CustomButton), new PropertyMetadata(OnBackgroundColorPropertyChanged)); /// <summary> /// BackgroundColorProperty property changed handler. /// </summary> /// <param name="d">CustomButton that changed its BackgroundColor.</param> /// <param name="e">DependencyPropertyChangedEventArgs.</param> private static void OnBackgroundColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CustomButton _CustomButton = d as CustomButton; if (_CustomButton != null) { OnPropertyChanged(_CustomButton, "BackgroundColor"); } } #endregion BackgroundColor #region INotifyPropertyChanged Members }

The DefaultStyleKey propery setted in the constructor tells what is the default style to load from the Generic.xaml file.

The interesting thing happens in the OnApplyTemplate() functions; here since we cannot use TemplateBinding to change the color of the GradientStop used in the RadialBrush that defines the background color of the button, we have to access to the element in code and change its value 'at runtime'.

I modified my xaml like this:

<Grid x:Name="LayoutRoot" Background="White"> <StackPanel> <StackPanel Orientation="Horizontal"> <Button Content="Edit1" Style="{StaticResource RoundTextButton}" /> <Button Content="Edit2" Style="{StaticResource RoundTextButton}" Background="{StaticResource YellowRadialBrush}" /> <Button Content="Edit3" Style="{StaticResource RoundTextButton}" Background="{StaticResource GreenRadialBrush}" /> <Button Content="Edit4" Style="{StaticResource RoundTextButton}" Background="{StaticResource RedRadialBrush}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <ctrl:CustomButton Content="Edit1" /> <ctrl:CustomButton Content="Edit1" BackgroundColor="Yellow" /> <ctrl:CustomButton Content="Edit1" BackgroundColor="Green" /> <ctrl:CustomButton Content="Edit1" BackgroundColor="Red" /> </StackPanel> </StackPanel> </Grid>

But this isnt enough yet since the first time I run the code I saw that the buttons weren't working well:

CustomButtons2

The 4 buttons were rendered circular but their color was not right and the animation didnt worked too...it tooks me a while (and the help of SilverlightDefaultStyleBrowser) to realize that by default every control is created in a DISABLED state and that you have to enable it (in the xaml style or in code).

<Style TargetType="ctrl:CustomButton"> <!-- key lines to enable the control --> <Setter Property="IsEnabled" Value="true" /> <Setter Property="IsTabStop" Value="true" /> ...

After placing the right lines in the default style in Generic.xaml it all finally worked and you can now have your buttons working with a more compact and readable xaml.

Example Solution:



Control, Silverlight, Window, Xaml, Custom control, Template

0 comments

Silverlight: simulate a 'Windows' desktop application - part 1

Print Content | More

As my first real experiment in building a Silverlight control, I wanted to realize something that could permit me to offer to the users a windows-like experience application hosted in the browser, so I started wondering how difficult was to implement a control that could host other silverlight control and even pages, this control should then act as a 'real' window and can be dragged on the surface (over other controls) and with resizing and closing capabilities, plus I want to give the Window a look similar to WIndows Vista Aero Desktop windows.

To be honest Silverlight is very flexible and its templating capabilities made the task a lot simplier than I tought at start, here's what we want to obtain:

SimulatingWindows1

This 'tutorial' will be divided in a series of 3 articles that describe:

  1. the basic idea and the creation of the control.
  2. implementation of dragging capabilities.
  3. implementation of resizing capabilities.

The source code that comes with each part will however include the complete example solution (since I'm too lazy to divide it in parts), so let's get started.

There are a bunch of articles out there that describes how to create a user control and how to skin it so I won't dig deep in that; one of the best and very simple to follow is: 'Craft Custom Controls for SIlverlight 2' by Jeff Prosise (http://msdn.microsoft.com/en-us/magazine/cc721611.aspx), so use it as you main reference in creating your new controls.

Let's point out the ideas that are behind the Window control:

To move the windows around and resize them freely we need to use the 'Canvas trick' and place an outside Canvas that encapsulate any other controls on the page in which we want to use the windows, this is a must since I've not yet found a way to inject dinamically a canvas on a preexisting page.

We then need 2 classes basically: a Window class that will handle the windows features and a WindowManager class that act like a supervisor for all the windows hosted on the page, this is a very simple class that act like the operating system do with normal windows...it creates them and when he receive the WM_CLOSE message (placed in the message pump by the windows) it closes them.

We are forced to implement our own mecanics since silverlight do not offer any native way (yet) to handle windows like we want to do, we also do not have a message pump to use so we will use the notification events that will be raised by the Window.

The Windows control is quite simple to realize at start, its main template will be contained in the Generic.xaml file of the solution and will be automatically loaded for each instance since we specify the DefaultStyleKey property in the constructor of the class, the template will define some pure graphical controls and 4 named controls that will represent:

  • the window surface itself - used for resizing the control
  • the caption bar - used for dragging the window around
  • the caption bar text
  • the close button.

The style for the close button is defined in the App.xaml file and redefines the template of a normal silverlight button, you can use Expression Blend to take a look at both files and edit the templates.

We will get a reference to those control when the template will be loaded by the runtime (just before rendering it) in the overridden OnApplyTemplate() function:

/// <summary> /// Gets called once the template is applied /// </summary> public override void OnApplyTemplate() { base.OnApplyTemplate(); window = GetTemplateChild("PART_Window") as Grid; captionBar = GetTemplateChild("PART_CaptionBar") as FrameworkElement; captionText = GetTemplateChild("PART_CaptionText") as TextBlock; captionText.Text = _Caption; Button closeButton = GetTemplateChild("PART_CloseButton") as Button; if (closeButton != null) closeButton.Click += new RoutedEventHandler(closeButton_Click); DefineDragEvents(); DefineResizeEvents(); Canvas.SetZIndex(this, currentZIndex); }

We also have to provide the window with an event that will be raised whrn the user click on the close button.

public event EventHandler Closed; public void Close() { this.Visibility = Visibility.Collapsed; //raise the closed event if (Closed != null) Closed(this, EventArgs.Empty); } void closeButton_Click(object sender, RoutedEventArgs e) { Close(); }

the handler will hide the window and notify the manager to take all the actions it has to do to remove the window from the surface and destroy it.

An intereresting thing to note is how it was implemented the Caption property, used to set the title of the window:

public string Caption { get { return (captionText != null) ? captionText.Text : _Caption; } set { if (captionText != null) captionText.Text = value; else _Caption = value; } } private string _Caption = "";

It may happen that you want to get or set this property before the template is actually loaded, so you have to
bufferize the value and apply it later in the OnApplyTemplate() function.

The WindowManager class is extremely simple, it will get a refence to the Canvas in which any window will be placed and will offer function to create new windows specifing the control that will be hosted inside and the position where to display it. It will internally subscribe to the Closed() event raised by the windows and will automatically remove them from the surface whenever they are closed.

/// <summary> /// Class that handles the creation and destruction of dynamic windows /// </summary> public class WindowsManager { private Canvas _canvas = null; /// <summary> /// creates the manager and stores the canvas to which attach the windows /// </summary> /// <param name="surface"></param> public WindowsManager(Canvas surface) { _canvas = surface; } public Window ShowWindow(FrameworkElement content, string caption, Point location) { Window w = new Window(); w.Caption = caption; w.Content = content; w.Closed += new EventHandler(w_Closed); Canvas.SetLeft(w, location.X); Canvas.SetTop(w, location.Y); _canvas.Children.Add(w); return w; } ...

void w_Closed(object sender, EventArgs e) { //remove the object from the childern colelction and dispose it dispose the object Window w = (Window)sender; _canvas.Children.Remove(w); //todo: dispose the object } }

You have a series of options on how to use this control, you can define it directly in xaml:

<Canvas x:Name="LayoutRoot" Background="White"> <ctrl:Window Width="200" Canvas.Top="100" Caption="Test 0"> <TextBlock>TEST</TextBlock> </ctrl:Window> ...

Or you can create them by code using the WindowManager class.

public partial class Page : UserControl { public Page() { InitializeComponent(); this.Loaded += new RoutedEventHandler(Page_Loaded); } void Page_Loaded(object sender, RoutedEventArgs e) { wm = new WindowsManager(LayoutRoot); this.btn.Click += new RoutedEventHandler(btn_Click); } int i = 0; void btn_Click(object sender, RoutedEventArgs e) { i++; wm.ShowWindow(new WindowContent(), "Windows " + i.ToString(), new Point(100, 100)); } private WindowsManager wm = null; }

 

That's all for this part, see you for the next ones.

The complete sample solution:

Technorati Tag: ,



Control, Silverlight, Window, Xaml

6 comments

Silverlight: How to Add a Context Menù to a FrameworkElement

Print Content | More

I want to be able to display a context menù or something else when my mouse is over a series of specified framework element on a given Silverlight Application; keep in mind we do not have support for mouse right click event due to a series of reasons that goes from difficulty of implementation on a cross browser platform to the fact that Mac users generally do not have it.

So we need to think of a little different approach and think of a UI element that can be used to display the menù or whatever we want. The idea I had was to Highlight the control (or framework element) that is under the mouse and display a clickable anchor on the top-right corner of the bounding box surrounding the control.

The anchor can then be clicked to display the menù. The effect I want to obtain is diplayed in the following picture, a blue bounding box surrounding the element and a button to use as anchor.

Extender2

The idea is quite simple, we need an external Canvas that will contain our main page and controls, we will dynamically create a Border and a Button that will be used for the Highlight and for the Anchor, these controls will be added to the canvas and will be resized and repositioned at runtime to fit over the control we have under the mouse. Also we will set the Z-Index of the controls to be greater of the control we want to 'extend', this way they will be displayed on top.

To diplay and remove the highlight we subscribe to the MouseEnter event of the control we want to extend and to the MouseLeave event of the bounding box (Border) used to Highlight the control.

All these function will be wrapped in an Helper class that will be instantiated on the main page, it will also have an AddContextMenu function that will be called for any FrameworkElement we want to extend with a context menu. The class is quite straightforward to implement and doesn't need too many comments, so here it is:

public class ContextMenuExtender { private Canvas _canvas = null; private Dictionary<FrameworkElement, ContextMenuAnchor> _anchors = new Dictionary<FrameworkElement, ContextMenuAnchor>(); private FrameworkElement _current = null; /// <summary> /// the external canvas that will be used to hold the controls /// </summary> /// <param name="canvas"></param> public ContextMenuExtender(Canvas canvas) { _canvas = canvas; } /// <summary> /// extends the framework element /// </summary> /// <param name="ctrl"></param> /// <param name="click"></param> public void AddContextMenu(FrameworkElement ctrl, RoutedEventHandler click) { if (!_anchors.ContainsKey(ctrl)) { ctrl.MouseEnter += new MouseEventHandler(_ctrl_MouseEnter); ContextMenuAnchor anchor = new ContextMenuAnchor(); _anchors.Add(ctrl, anchor); anchor.button.Click += click; anchor.button.Click += new RoutedEventHandler(button_Click); anchor.border.MouseLeave += new MouseEventHandler(b_MouseLeave); } } void button_Click(object sender, RoutedEventArgs e) { RemoveHighlight(sender as Button); } void _ctrl_MouseEnter(object sender, MouseEventArgs e) { HighlightControl(sender as FrameworkElement); } /// <summary> /// reposition the border and the anchor over the control /// </summary> /// <param name="ctrl"></param> void HighlightControl(FrameworkElement ctrl) { //this is a patch to have only 1 element highlited at a time, // sometimes if you move the mouse //too fast, silverlight misses to raise the MouseLeave event. if ((_current != null) && (_current != ctrl)) RemoveHighlight(_current); _current = ctrl; ContextMenuAnchor anchor = _anchors[ctrl]; if (anchor != null) { Button btn = anchor.button; Border b = anchor.border; if (!_canvas.Children.Contains(b)) { GeneralTransform gt = ctrl.TransformToVisual(_canvas); Point p = gt.Transform(new Point(0, 0)); b.Width = ctrl.ActualWidth; b.Height = ctrl.ActualHeight; b.SetValue(Canvas.TopProperty, p.Y); b.SetValue(Canvas.LeftProperty, p.X); _canvas.Children.Add(b); Canvas.SetZIndex(b, Canvas.GetZIndex(ctrl) + 1); btn.SetValue(Canvas.TopProperty, p.Y); btn.SetValue(Canvas.LeftProperty, p.X + ctrl.ActualWidth - btn.Width); _canvas.Children.Add(btn); Canvas.SetZIndex(btn, Canvas.GetZIndex(ctrl) + 1); } } } void b_MouseLeave(object sender, MouseEventArgs e) { RemoveHighlight(sender as Border); } #region RemoveHighlight functions void RemoveHighlight(Border ctrl) { ContextMenuAnchor anchor = (from r in _anchors where r.Value.border == ctrl select r.Value).Single(); RemoveHighlight(anchor); } void RemoveHighlight(Button ctrl) { ContextMenuAnchor anchor = (from r in _anchors where r.Value.button == ctrl select r.Value).Single(); RemoveHighlight(anchor); } void RemoveHighlight(FrameworkElement ctrl) { RemoveHighlight(_anchors[ctrl]); } void RemoveHighlight(ContextMenuAnchor anchor) { _canvas.Children.Remove(anchor.border); _canvas.Children.Remove(anchor.button); } #endregion /// <summary> /// this class will contain out anchor and highlight controls /// </summary> private class ContextMenuAnchor { public Border border = new Border(); public Button button = new Button(); public ContextMenuAnchor() { button.Width = 10; button.Height = 10; border.BorderBrush = new SolidColorBrush(Colors.Blue); border.BorderThickness = new Thickness(1); } } }

 

The class can be used like follows:

<UserControl x:Class="ContextMenuExtender.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Canvas x:Name="surface"> <StackPanel x:Name="LayoutRoot" Background="White" Margin="10"> <StackPanel Orientation="Horizontal"> <TextBlock Margin="0,0,10,0">TEST</TextBlock> ...

 

public partial class Page : UserControl { public Page() { InitializeComponent(); ContextMenuExtender cm = new ContextMenuExtender(surface); cm.AddContextMenu(txt, btn_Click); cm.AddContextMenu(ell, btn_Click); cm.AddContextMenu(txt2, btn_Click); cm.AddContextMenu(ell2, btn_Click); } ...

 

The example is far to be a complete reusable control (it misses skinning and customization options for the highlight and the anchor button), but can be used as a stating point for your ContextMenuExtender.

Attached to this article there's a complete working example:

Technorati Tag: ,



Control, Menu, Silverlight, Window, Xaml, Context menu

3 comments

Silverlight: The Power of <Canvas>

Print Content | More

The Canvas is usually an underestimated layout control in Silverlight, instead it's really useful and flexible; one the most usefull things I have discovered while playing with Silverlight is to build pages using a Canvas as the most external container and place any other layout control inside it.

Why? it has a very nice and easy support for Z-Indexing and if you diplay some controls/forms/whatever on top of each other is a very simple task to realize. You can easily build a window-like desktop application in this way.

So here's the way in which I usually define my silverlight pages/applications.

<UserControl x:Class="ContextMenuExtender.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Canvas x:Name="surface"> <StackPanel x:Name="LayoutRoot" Background="White"> ... </StackPanel> </Canvas> </UserControl>

Technorati Tag: ,,


Control, Silverlight, Window, Xaml, Canvas, Tip

0 comments

Back to Action!

Print Content | More
From time to time I really think that I should start to blog things seriously, but I have to admitt I'm just to lazy to do things properly.

This will be a new start for me and I really hope to carry the whole thing on, so from now on less playing with World of Warcraft and more software developing (maybe).

Due to my 'not so good' english many articles and posts will be subject to continous revisions...cause I really dislike to make mistakes :D.

5 comments

How to start the debugger for a website in VS2003 after installing IE7

Print Content | More
If you get an error trying to start the debugger for a website in VS2003 after the installazion of IE7 caused by the new security settings in the browser, try the following steps maybe they can resolve your problem:
  1. Check that the site is using the 'Integrated Windows Authentication' (in IIS, site properties, Directory Security, Anonymous Access and authentication control).
  2. The user under which runs the site is a member of the 'Debugger Users' group.
  3. Verify that the same user has Full Control access right on the directory in the file system.
  4. Check the we.config to see if the debug flag is setted to true.
  5. In IE7, internet options, Security Tab, under Local Intranet add the site address (usually http://localhost/)
  6. (optional) In IE7, internet options, Security Tab, under Local Intranet, press the Custom Level button and set 'Automatic Logon with current username and password' for the 'User Authentication\Logon' setting.


Iis, Integrated windows authentication, Security settings

2 comments

 18 di 18   « First  «