How do you write verbatim XAML code through c# codebehind in WPF? - c#

I would like to add pure XAML code into my xaml elements during runtime. Does anyone know how to do that? thank you.
I would like to do something like this: myGrid.innerXAML = stringXAMLcode
that would result to <grid name="myGrid">newgeneratedcodehere</grid>
in PHP you can print verbatim HTML code directly into the HTML file. Is this possible with c#?
if not, can anyone suggest a work-around?
thanks!

There are ways to do what you're asking here, as explained in this CodeProject Article:
Creating WPF Data Templates in Code: The Right Way
However, most of the time you really don't need that for daily operations.
If you're working with WPF, you really need to leave behind the traditional approach from other frameworks and embrace The WPF Mentality.
HTML (4, 5 or whatever) looks like a ridiculous joke when compared to the WPF implementation of XAML, therefore all the horrendous hacks you might be used to in HTML are completely unneeded in WPF, because the latter has a lot of built-in features that help you implement advanced UI capabilities in a really clean way.
WPF is heavily based on DataBinding and promotes a clear and well-defined separation between UI and Data.
For example, this would be what you would do to when you want to "show different pieces of UI" depending on the Data, by using a WPF feature called DataTemplates:
XAML:
<Window x:Class="MyWindow"
...
xmlns:local="clr-namespace:MyNamespace">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Person}">
<!-- this is the UI that will be used for Person -->
<TextBox Text="{Binding LastName}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Product}">
<!-- this is the UI that will be used for Product -->
<Grid Background="Red">
<TextBox Text="{Binding ProductName}"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<!-- the UI defined above will be placed here, inside the ContentPresenter -->
<ContentPresenter Content="{Binding Data}"/>
</Grid>
</Window>
Code Behind:
public class MyWindow
{
public MyWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel
{
public DataObjectBase Data {get;set;} //INotifyPropertyChanged is required
}
Data Model:
public class DataObjectBase
{
//.. Whatever members you want to have in the base class for entities.
}
public class Person: DataObjectBase
{
public string LastName {get;set;}
}
public class Product: DataObjectBase
{
public string ProductName {get;set;}
}
Notice how I'm talking about my Data and Business Objects rather than worried about any hacks to manipulate the UI.
Also notice how defining the DataTemplates in XAML files that will get compiled by Visual Studio gives me compile-time checking of my XAML as opposed to putting it together in a string in procedural code, which of course doesn't have any kind of consistency checks.
I strongly suggest you read up on Rachel's answer (linked above) and related blog posts.
WPF Rocks

Why don't you add exactly the elements you want? Something like:
StackPanel p = new StackPanel();
Grid g = new Grid();
TextBlock bl = new TextBlock();
bl.Text = "This is a test";
g.addChildren(bl);
p.addChildren(g);
You can do this for all the elements that exist in the XAML.
Regards

You can use XamlReader to create the UIElement that you can set as child of content control or layout containers:
string myXamlString = "YOUR XAML THAT NEEDED TO BE INSERTED";
XmlReader myXmlReader = XmlReader.Create(myXamlString);
UIElement myElement = (UIElement)XamlReader.Load(myXmlReader);
myGrid.Children.Add(myElement );

Related

Caliburn Micro -> Composing Views from multiple Views/UserControls/CustomControls

How is it possible to re-use and compose parts in CM managed windows? I have found posts regarding using two UserControls to bind to the same ViewModel, but not so much if I want to have multiple views and viewmodels all composed in the same window. (a viewmodel for each view composed into a "master view")
The first part of my question would be how to break up components for re-use? If I have two areas of a window where one is a datagrid and another is a details view with labels and text boxes should these be in separate usercontrols, customcontrols or windows? Each one would ideally be stand alone so they can be separated and used in other windows.
So I would end up with 2 viewmodels and 2 views if they were separated. Now lets say I would like to create 3 windows, one window with the first view, the second with the second view and a third with both views. How do I use CM to create the window for each and wire up each view to their viewmodel? From the examples I have seen I see for the most part a single view and viewmodel in a window.
I'm not going to claim to be an expert in CM by any means, but I've had reasonable success with a simple "benchmark explorer" I've been writing. That uses a single "shell view" that composes two other views, each with its own ViewModel. The shell view looks like this:
<Window x:Class="NodaTime.Benchmarks.Explorer.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NodaTime Benchmarks" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
<ContentControl x:Name="BenchmarkPicker" Grid.Column="0"/>
<GridSplitter ... />
<ContentControl x:Name="ResultsGraph" Grid.Column="2"/>
</Grid>
</Window>
then ResultsGraphView and BenchmarkPickerView are each like this:
<UserControl x:Class="NodaTime.Benchmarks.Explorer.Views.ResultsGraphView"
... namespaces etc ...>
<Grid>
<Grid.RowDefinitions>...</Grid.RowDefinitions>
<Grid.ColumnDefinitions>...</Grid.ColumnDefinitions>
... controls ...
</Grid>
</UserControl>
The ShellViewModel exposes the other two ViewModels as properties. Those are then passed to the views automatically on construction. (The bootstrapper doesn't provide any way of getting them.)
Now this doesn't quite fit your description, because I don't think you could use the two individual views individually as windows - I suspect you would end up with 5 views in total:
SubViewOne - a UserControl with the first view parts
SubViewTwo - a UserControl with the second view parts
JustViewOne - a Window containing just SubViewOne
JustViewTwo - a Window containing just SubViewTwo
BothViews - a Window containing both SubViewOne and SubViewTwo
I don't think there's a way of getting around the fact that you don't want one Window within another, and the top level window has to be... well, a Window.
Hope this helps, and let me know if you want more details of the small project where I'm doing this - it's far from production quality, particularly in terms of DI, but it may be enough to help you get going.
I think I've previously done something similar to what you're asking. I'd been playing around with one of the TabControl with the intention of hosting several different tools for a game I enjoy playing.
The main tool is an item browser similar to the usual file explorer type programs, and similar to what Jon has described above. I'll explain some of the parts which may be of interest/relevance (I've removed some of the slightly obscure naming).
The main ExplorerView tab is essentially exactly the same the one Jon describes (which is hopefully a good sign - means I'm not crazy =D)
<UserControl x:Class="ItemsBrowser.Views.ItemsTabView"
<!-- namespaces -->
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="ItemsExplorer" Grid.Column="0" Grid.Row="0" />
<GridSplitter HorizontalAlignment="Right" VerticalAlignment="Stretch"
ResizeBehavior="PreviousAndNext" Width="4" Grid.Column="1" Background="#FFAAAAAA" />
<ContentControl x:Name="PanelView" Grid.Column="2" Grid.Row="0" />
</Grid>
</UserControl>
The associated ViewModel holds two other ViewModels, used for composing the main explorer view:
public class ItemsTabViewModel : Conductor<IScreen>.Collection.AllActive
{
public ItemsViewModel ItemsExplorer { get; set; }
public ExplorerPanelViewModel PanelView { get; set; }
// Ctor etc.
}
The ItemsExplorer hosts a TreeView style control, allowing users to explore various categories of Item from the game. This is used in multiple places in the application, and is composed into a few different controls.
The ExplorerPanelView is a panel on the right hand side, that changes to display a number of ViewModels, based on what type of item the user is viewing. The user also have the option to toggle a few different Views over the ViewModel displayed in the ExplorerPanelView.
The ExplorerPanelView looks like:
<UserControl x:Class="MIS_PTBrochure.Views.ExplorerPanelView"
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:cal="http://www.caliburnproject.org">
<Grid>
<ContentControl cal:View.Model="{Binding Path=ActiveItem}"
cal:View.Context="{Binding Path=ActiveItem.State}"
Content="Select a folder."/>
</Grid>
</UserControl>
And the ExplorerPanelViewModel behind:
public class ExplorerPanelViewModel : Conductor<IScreen>.Collection.OneActive,
IHandle<ItemSelectedEvent> // More events.
{
public ItemViewModel ItemInfo { get; set; }
public CategoryFolderViewModel CategoryFolderInfo { get; set; }
public ExplorerPanelViewModel()
{
// My helper to access the `Caliburn.Micro` EventAggregator.
EventAggregatorFactory.EventAggregator.Subscribe(this);
// Other code
}
public void Handle(ItemSelectedEvent message)
{
// Other code to check active status
ItemInfo = message.selected;
ActivateItem(ItemInfo);
}
protected override void OnDeactivate(bool close)
{
Debug.WriteLine("Deactivated " + this.ToString() + close.ToString());
if (close) { EventAggregatorFactory.EventAggregator.Unsubscribe(this); }
base.OnDeactivate(close);
}
// Other code
}
I've tried to remove a lot of non-relevant code. Essentially I'm again hosting multiple ViewModels as properties (although you could hold a collection) and activating the relevant ViewModel when an approriate event is raised by my ItemsExplorerViewModel. I'm using the Caliburn.Micro EventAggregator to handle communication between multiple ViewModels.
In theory you could dispense with the properties, and just activate the ViewModels referenced in the events themselves.
Regarding the cal:View.Context and cal:View.Model - I'm using these all the user to toggle different available View states available (each ViewModel displayed in that panel inherits from a base ViewModel class which all have a State property).
There are a few places where I pop up different windows using some of the same Views and ViewModels. To achieve this, I make use of the Caliburn.Micro WindowManager. There isn't a great deal about it in the official documentation (you're best off searching Google and the CM discussions), it pretty does what is says on the tin.
If you have a look at the Caliburn.Micro.IWindowManager interface you'll see some handy methods that you can call from a WindowManager instance.
public interface IWindowManager
{
bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
}
So to pop up a new Window with a ViewModel of your choice, I did something along these lines:
// Some basic Window settings.
dynamic settings = new ExpandoObject();
settings.Title = "Test Window";
settings.WindowStartupLocation = WindowStartupLocation.Manual;
settings.SizeToContent = SizeToContent.Manual;
settings.Width = 450;
settings.Height = 300;
var TestViewModel new TestViewModel();
WindowManagerFactory.WindowManager.ShowWindow(this.classSearch, null, settings);
Caliburn.Micro should again, resolve your Views to the correct ViewModels.
Hopefully there's something useful in there somewhere. I sort of arrived at this solution through a few design iterations, so this may not be the optimal approach to some of these problems. If anyone has any constructive criticism, please let me know =D

How to bind xml to wpf treeview?

How to bind xml to wpf treeview? I am using Prism mvvm pattern. I will prefer an IList for holding the data for looping.
I have tried http://geeklyeverafter.blogspot.com/2010/03/wpf-treeview-bound-to-xml-file.html and
http://www.blogs.intuidev.com/post/2009/12/28/xml_to_treeview.aspx
but nothing worked.
OK. It is The question is quite old now, but I think there is a simple way to bind XML to a TreeView. Maybe it is helpful for someone.
XAML:
<Window.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Elements}" x:Key="NodeTemplate">
<Grid>
<TextBlock Text="{Binding Path=Name}"/>
</Grid>
</HierarchicalDataTemplate>
</Window.Resources>
...
<TreeView x:Name="myTreeView" Grid.Column="0"
ItemsSource="{Binding Path=Root.Elements}"
ItemTemplate="{StaticResource ResourceKey=NodeTemplate}"
/>
In the code behind I just create a XDocument (System.Xml.linq) and bind this one to the DataContext of the TreeView. For example like this:
private XDocument _theXML;
public XDocument TheXML {
get => _theXML;
set => _theXML = value;
}
public MainWindow()
{
...
InitializeComponent();
DataContext = this;
TheXML = XDocument.Load(#"c:\file.xml");
myTreeView.DataContext = TheXML;
myTreeView.UpdateLayout();
}
That's it. The content of the XML file will be shown as a TreeView. If you like to see some more Details (Attributes, ...) you can refine the Template in the XAML code.
The way I've done it is to create a method that builds the tree view to a treeview property. Set the WPF treeview items binding to the items property of the treeview property in your class. Of course implementing INotifyPropertyChanged in your ViewModelBase is essential.
I would be happy to give an example, but I do not have access to the internet on my PC at the moment
I did see in the post and do agree that this is not the most proficient way . However since you are already using xml serialization, the parsing of xml is done, now you just have to use the data.
I think that if you were not going to serialize, then the links you posted would hold more validity in the methodology you are you are trying to achieve. But that is just IMO. I will update with some working code when I get the chance tomorrow, the idea of data binding directly to xml sounds fun.
In the meantime check this link out. It looks pretty strait forward.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/cbdb2420-1403-436f-aa7f-b1e3b1acb398/binding-any-xml-document-to-wpf-treeview?forum=wpf

Binding to code behind from custom control

I have a GridView that has several buttons. One of them is defined by the following template:
<DataTemplate x:Name="SubjectItemTemplate">
<Canvas Width="340" Height="170" VerticalAlignment="Top">
<Controls:ThreeImageButton HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"
NormalStateImageSource="{Binding NormalImage}"
HoverStateImageSource="{Binding HoverImage}"
PressedStateImageSource="{Binding PressedImage}" Command="{Binding Path=NavigateToUnitsPage}"
CommandParameter="{Binding}" Canvas.Left="0" Canvas.Top="0">
</Controls:ThreeImageButton>
</Canvas>
</DataTemplate>
Now I have a custom control as you can see, called ThreeImageButton. The button works fine when I use it on its own. But when I have it in the DataTemplate it won't bind properties to the code behind.
Right now, I have
x:Name="MyThreeImageButton"
in the custom button definition. And I connect to the code-behind like this:
<TextBlock Text="{Binding ElementName=MyThreeImageButton, Path=NormalStateImageSource}"/>
(This is just a test to display the text, in the actual code I would assign an image source to another property that is referred to by an element).
Right now, nothing is displayed in the TextBlock. What is the correct binding syntax I'm supposed to use to reach my properties?
Thanks!
Edit: I am setting the variable in the InitializeComponent function and I am using SetValue on the DependencyProperty.
Edit: Let me add the following information to be more clear
Scenario I:
In DataTemplate for GridView:
<UserControl CustomParameter="Literal Text">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
works!
Scenario II:
In DataTemplate for GridView:
<UserControl CustomParameter="{Binding ValueFromDataItem">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
nope!
I see,
So setting up a two-way binding to a custom property in a user control can be tricky because a user control cannot bind to a CLR property. Not only that but setting the data context on a user control has an unexpected effect on the binding inside it.
You can solve these problems with a little slight of code. Basically back your CLR properties with dependency properties and set the data context on a child element instead of the root user control.
Take a look at this sample. Let's pretend you have the following MainPage. That MainPage will eventually use our custom user control. So let's set the stage.
Here's the code-behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = new /* your view model */
{
Title = Guid.NewGuid().ToString(),
};
}
}
In the code above I am simulating a complex view model with a simple anonymous class. It would be silly for you to implement your own like this, but at the same time it is silly for me to build a simple sample with the complete scaffolding. I bring this up only so it does not confuse you - as it could look like I am suggesting this approach in prod.
Here's the XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl Text="{Binding Title}" />
</Grid>
In the XAML above, there is absolutely nothing special. I already have reference to the user control in the local namespace and I simply declare it here.
Okay, now that we have a consumer of the control, it's worth pointing out that in testing developers can mistakenly think that their binding is working because they test with literal values. Literal values bind fine. It's binding from the underlying view model that hick-ups.
Let's say another thing, some developers tend to avoid dependency properties because the require a little more typing. People remember that [kbd]propdp[/kbd] is a handy Visual Studio snippet that stubs out a dependency property for you.
Take a look at this user control. It has two controls, a TextBox and a TextBlock which are there to demonstrate the OneWay and TwoWay functionality of this binding approach. We also implement INotifyPropertyChanged on the user control. For the most part, adding a view model in the case of a user control is overkill because the user control already acts like a view model. It's up to the developer, but it seems dumb to me.
Here's the code behind:
public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
public MyUserControl()
{
this.InitializeComponent();
}
// text property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValueDp(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl), null);
// bindable
public event PropertyChangedEventHandler PropertyChanged;
void SetValueDp(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
SetValue(property, value);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In the ode above, I have create a "Text" property and backed it with a dependency property. For a matter of reuse I have also implemented SetValueDp() which could be used again and again if I had more than a single property. Even though this demo has but one, I wanted to include this because the repetitive logic should certainly be abstracted out like this.
Here's the XAML:
<Grid Background="Black" DataContext="{Binding ElementName=userControl}">
<StackPanel>
<TextBox Text="{Binding Text, Mode=TwoWay}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
<TextBlock Text="{Binding Text}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
</StackPanel>
</Grid>
In the XAML above, I do nothing special insofar as binding. The syntax simply binds to the Text property using the Mode appropriate to the control. Just like you would do normally. However, what's worth noticing is that the DataContext is NOT set on the user control. Instead, it is set on the Grid. As a point of fact, any control in the tree other than the user control could be used like this. Just don't set the data context of the user control.
That is it by the way.
I have tested it to make sure it works. Demonstrating both one and two way binding is pretty handy here. I might even turn this into a blog in case other developers want to find it and don't discover this question. Thanks for your question!
Best of luck!
As the comments alluded to, your DataTemplate is placing the datacontext of the items to whatever object you are adding to your list. This is not the same as the surrounding user control's data context. If you want to reference that datacontext's commands, do the following in the DataTemplate's bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.NormalImage}
What this is saying is to go out and find the user control ancestor and use its datacontext and then look for the NormalImage property. If you run into problems, check your output window for binding errors. It is very helpful in finding binding problems.

How to set DataTemplate for class inheriting more than one interface?

I am trying to use an MVVM architecture and MEF in order to build by application. I want to use DataTemplates and ContentControls in order to allow my application to display to the user in as generic a way as possible.
So I am now creating the ability for a user to read/write information and read/write results to somewhere, could be XML, could be a database. So I have two interfaces IResultStorage and ITestStorage
I now want to create a page for the user to update settings for these, so file location or database etc. My view model imports them via MEF:
public sealed class AdminViewModel : ViewModelBase
{
[Import]
public ITestStorage TestStorage { get; set; }
[Import]
public IResultStorage ResultStorage { get; set; }
}
Then the view is exported and loaded into the Resources.MergedDictionaries at run time
<DataTemplate DataType="{x:Type vm:AdminViewModel}">
<Grid>
<TabControl Grid.Row="0">
<TabItem Header="Tests">
<ContentControl Grid.Row="0" Content="{Binding TestStorage}"/>
</TabItem>
<TabItem Header="Results">
<ContentControl Grid.Row="0" Content="{Binding ResultStorage}"/>
</TabItem>
</TabControl>
</Grid>
</DataTemplate>
However, the way I currently have it implemented is that one class has inherited both of these and it is this that is causing me problems:
[Export(typeof(ITestStorage))]
[Export(typeof(IResultStorage))]
public sealed class XmlStorage : ITestStorage, IResultStorage { ... }
So when the AdminViewModel above gets drawn both ContentControls are of type XmlStorage it seems so I don't know how to create DataTemplates to draw them properly.
Hopefully this makes sense, if I have done it totally the wrong way it would be good to know.
Well for a more tricky implementation, let us called it more intelligent implementation I would suggest a TemplateSelector. For more information please have look here.
You will be able to assign templates based on the type of the given VM or business object. The only challenge you will face is the fact that you have to find out in which 'role' the object is passed to the TemplateSelector.
Additional info
I think this will help you, too.
have you testet if you create subDatatemplates for each as Resourse?
<DataTemplate DataType="{x:Type vm:TestStorage}">
<Grid>
<Label Content="{Binding someValueFromTestStorage}"/>
</Grid>
</DataTemplate>
EDIT
maybe this 2 links could help you First, Second (ger)
also this link could be interesting follow Beatriz Costa - MSFT (Partner)

Decouple the screens without magic strings

My WPF project will be organised like this :
Screens
Group1
Screen1
View.xaml
ViewModel.cs
Group2
Screen2
View.xaml
ViewModel.cs
To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.
How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)
Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.
Update :
I came up with this:
public class ScreenNames
{
public Group1Screens Group1;
public class Group1Screens
{
public ScreenName Screen1;
}
}
public sealed class ScreenName
{
private ScreenName() { }
}
public class ScreenManager : IScreenManager
{
public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}
Usage:
screenManager.Show(x=>x.Group1.Screen1);
Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.
You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.
You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.
public class RootViewModel : INotifyPropertyChanged
{
public object Screen1ViewModel { get; }
public object Screen2ViewModel { get; }
}
Databind one ContentPresenter control to the Screen1ViewModel property using
<ContentControl Content="{Binding Path=Screen1ViewModel}" />
and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.
The DataTemplates may be as simple as this:
<Window.Resources>
<DataTemplate DataType="{x:Type foo:MyViewModel}">
<self:MyControl />
</DataTemplate>
<DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
<self:MyOtherControl />
</DataTemplate>
</Window.Resources>
In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.
Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application

Categories