How do I get WPF searchbox styling like GoogleChrome browser search? - wpf

I am planning to have search functionality in WPF like it happens in Google Chrome browser. The sample is shown below
I have the backend code ready, but I want to have a TextBox like the one shown below - in which I can display the results also(like 0 of 0).
Also I would like to have the arrow marks for next and prev.
How do I design such a TextBox in WPF in XAML? Please guide me regarding the same.

A custom control can be created using following code:
public class SearchTextBox : Control
{
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(SearchTextBox), new UIPropertyMetadata(null));
public String SearchStatusText
{
get { return (String)GetValue(SearchStatusTextProperty); }
set { SetValue(SearchStatusTextProperty, value); }
}
// Using a DependencyProperty as the backing store for SearchStatusText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SearchStatusTextProperty =
DependencyProperty.Register("SearchStatusText", typeof(String), typeof(SearchTextBox), new UIPropertyMetadata(null));
static SearchTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchTextBox), new FrameworkPropertyMetadata(typeof(SearchTextBox)));
}
}
Style in Generic.xaml
<Style TargetType="{x:Type local:SearchTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SearchTextBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Text="{TemplateBinding Text}" />
<TextBlock Grid.Column="1"
Text="{TemplateBinding SearchStatusText}"></TextBlock>
<Button Grid.Column="2">
<Polyline Points="0,10 5,0 10,10"
Stroke="Black"
StrokeThickness="2" />
</Button>
<Button Grid.Column="3">
<Polyline Points="0,0 5,10 10,0"
Stroke="Black"
StrokeThickness="2" />
</Button>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You will need to change it according to your needs. But this should be a good starting point.

There is no Ready made solution for your requirement.You have to fiddle quite a few with the controltemplates of existing controls or you can build a custom control from scratch.
http://msdn.microsoft.com/en-us/library/aa970773%28VS.85%29.aspx

Related

Force a repaint of a custom drawn UIElement in a custom WPF control

I'm working on a custom WPF control. The main purpose of this control is to visualize thousands of graphical primitives in a scrollable area. The core part of the control's template looks like this:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ItemVisualizer}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<local:ItemAreaElement Grid.Row="0" Grid.Column="0" x:Name="PART_ItemArea" />
<ScrollBar Grid.Row="0" Grid.Column="1" x:Name="PART_ScrollBarVert" Orientation="Vertical" Maximum="100" />
<ScrollBar Grid.Row="1" Grid.Column="0" x:Name="PART_ScrollBarHorz" Orientation="Horizontal" Maximum="100" />
<Rectangle Grid.Row="1" Grid.Column="1" x:Name="PART_SizeGrip" Focusable="False" Fill="#F0F0F0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
The ItemAreaElement is responsible for drawing the items. For simplicity, we can think that its core part looks like this:
class ItemAreaElement : FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
for (int i = 0; i < _data.ItemCount; i++)
{
drawingContext.DrawLine(_penLine, new Point(0, i * 10), new Point(100, i * 10));
}
}
}
I need to repaint the ItemAreaElement every time when a related property in the whole ItemVisualizer control changes. However, I didn't find a way to do that in WPF. The well know trick with the Dispatcher object does not work in my case:
private static void OnItemCountPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
ItemVisualizer vis = (ItemVisualizer)source;
vis._itemArea.Dispatcher.Invoke(delegate { }, DispatcherPriority.Render);
}
, where _itemArea is a local reference to the ItemAreaElement got in OnApplyTemplate():
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.Template != null)
{
_itemArea = this.Template.FindName("PART_ItemArea", this) as ItemAreaElement;
_itemArea.Grid = this;
}
}
Are there other ways to force an update of the UIElement in my construction? Or maybe, I need to redesign the whole control to make it possible?
It should usually be sufficient to specify FrameworkPropertyMetadataOptions.AffectsRender on registration of the ItemCount property:
public static readonly DependencyProperty ItemCountProperty =
DependencyProperty.Register(
"ItemCount", typeof(int), typeof(ItemVisualizer),
new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.AffectsRender));
If that doesn't help, you could force a redraw by calling InvalidVisual() on the ItemAreaElement:
var vis = (ItemVisualizer)source;
vis._itemArea.InvalidVisual();

Control inheritance and blendability

I created a custom control which inherits from Window. The goal is to make a reusable window for all the small apps I'll program in my company, and not having to redo the header/footer/base styles... each time.
So here is what I did.
The class:
public class MyWindow : Window
{
public bool ShowHeader
{
get { return (bool)GetValue(ShowHeaderProperty); }
set { SetValue(ShowHeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowHeader. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowHeaderProperty =
DependencyProperty.Register("ShowHeader", typeof(bool), typeof(MyWindow), new PropertyMetadata(true));
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
// Using a DependencyProperty as the backing store for HeaderText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderTextProperty =
DependencyProperty.Register("HeaderText", typeof(string), typeof(MyWindow), new PropertyMetadata(""));
public MyWindow()
{
this.Template = FindResource("MyWindowTemplate") as ControlTemplate;
}
}
The template:
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type me:MyWindow}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Images/logo.gif" />
<TextBlock Grid.Column="1" Text="{TemplateBinding HeaderText}" />
</Grid>
<ContentPresenter />
</DockPanel>
</Border>
</ControlTemplate>
Those 2 things are in a class library.
In my app, I have set this.
app.xaml:
<Application x:Class="TestInheritance.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1;component/MyStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
and my MainWindow.xaml is:
<uc:MyWindow x:Class="TestInheritance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:ClassLibrary1;assembly=ClassLibrary1"
Title="MainWindow"
Width="525"
Height="350"
HeaderText="Test1234"
ShowHeader="True">
<Grid>
<TextBlock Text="test text" />
</Grid>
</uc:MyWindow>
All of this works as intended in execution, but in the designer, I don't see the header (no matter the value I set for ShowHeader), neither the HeaderText.
What should I do to make all this "blendable", and see the header in the designer ?
EDIT
I tried moving the ControlTemplate in a style
<Style TargetType="{x:Type me:MyWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type me:MyWindow}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="Images/logo.gif" />
<TextBlock Grid.Column="1" Text="{TemplateBinding HeaderText}" />
</Grid>
<ContentPresenter />
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and use this
StyleProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(FindResource(typeof(MyWindow))));
in Mywindow's constructor.
So now, I have the opposite, I see the header in design mode, but not in the application anymore...
EDIT2
I moved the style in Themes\Generic.xaml and added [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] in the Assemblyinfo.cs of my class library.
I also added a static constructor to my class like this:
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
and it still doesn't work.
So now my solution structure is the following: (non-relevant parts omitted)
Solution
ClassLibrary1
Properties
AssemblyInfo.cs
Themes
Generic.xaml
MyWindow.cs
TestInheritance (the wpf app)
App.xaml
MainWindow.xaml
Edit3
Here is the link to my code: https://github.com/MerlinDuChaos/TestInheritance/
Set the default style in static constructor like this
static MyWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyWindow), new FrameworkPropertyMetadata(typeof(MyWindow)));
}
Also dont forget to set this stupid little property:
<Style TargetType="{x:Type me:MyWindow}">
<Setter Property="Background" Value="White"/>
Try it out now
EDIT:
public partial class App : Application
{
//public App()
//{
// InitializeComponent();
// FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata
// {
// DefaultValue = FindResource(typeof(Window))
// });
//}
}
EDIT 2:
To force designer apply your custom window style you shall copy paste this inside app xaml.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:/ClassLibrary1;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

How to get image source in Control Template

I've added one button like this:
<Button x:Name="myButton"
Style="{StaticResource myButtonStyle}"
Height="36"
VerticalAlignment="Top"
Click="myButton_Click">
<Grid>
<Image Height="*" Width="31" Source="{Binding Path=Image}" />
<TextBlock Text="{Binding Path=DisplayName}" HorizontalAlignment="Center" />
</Grid>
</Button>
Where 'Image' is the source of the required image in string. And the style is as follows:
<Style x:Key="myButtonStyle"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="myButtonRootBorder">
<StackPanel Orientation="Horizontal">
<Image Source="{??}" Width="{??}" Height="{??}" />
<!--ContentPresenter-->
<ContentPresenter Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}" />
</StackPanel
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now how to set the height, width and source of the image in the control template.
Please help. Thanks in advance.
For width and height you can use {TemplateBinding Width} and {TemplateBinding Height}. If you want the image to be customizable you should inherit the button class and add an imagesource property:
public class ImageButton : Button {
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));
}
Then you can use {TemplateBinding ImageSource} for the image.

WPF popup: how to make a reusable template for popups?

Since Popup doesn't derive from Control and doesn't have a template, how can I define a template so that all popups look the same? I need to design one that has a certain look and don't want to have to copy markup each time one is used.
This seems pretty easy but I can't figure out how to do it. The Child property defines a logical tree but I don't see how you can pull that out into a template and reuse it.
I was looking to do the same thing and here is what I came up with:
I inherited from ContentPresenter, styled that control as I wanted and than placed the derived ContentPresenter inside my Popup, I only used 2 text blocks for the simplicity but it is easy to understand how any content could be added.
My custom control:
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
[TemplatePart(Name = PART_PopupHeader, Type = typeof(TextBlock))]
[TemplatePart(Name = PART_PopupContent, Type = typeof(TextBlock))]
public class CustomPopupControl : ContentControl
{
private const string PART_PopupHeader = "PART_PopupHeader";
private const string PART_PopupContent = "PART_PopupContent";
private TextBlock _headerBlock = null;
private TextBlock _contentBlock = null;
static CustomPopupControl()
{
DefaultStyleKeyProperty.OverrideMetadata
(typeof(CustomPopupControl),
new FrameworkPropertyMetadata(typeof(CustomPopupControl)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_headerBlock = GetTemplateChild(PART_PopupHeader) as TextBlock;
_contentBlock = GetTemplateChild(PART_PopupContent) as TextBlock;
}
public static readonly DependencyProperty HeaderTextProperty =
DependencyProperty.Register("HeaderText", typeof(string), typeof(CustomPopupControl), new UIPropertyMetadata(string.Empty));
public string HeaderText
{
get
{
return (string)GetValue(HeaderTextProperty);
}
set
{
SetValue(HeaderTextProperty, value);
}
}
public static readonly DependencyProperty ContentTextProperty =
DependencyProperty.Register("ContentText", typeof(string), typeof(CustomPopupControl), new UIPropertyMetadata(string.Empty));
public string ContentText
{
get
{
return (string)GetValue(ContentTextProperty);
}
set
{
SetValue(ContentTextProperty, value);
}
}
}
}
Style for the control:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControls">
<Style TargetType="{x:Type local:CustomPopupControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomPopupControl}">
<Border CornerRadius="3" BorderThickness="1" BorderBrush="White">
<Border.Background>
<SolidColorBrush Color="#4b4b4b" Opacity="0.75"/>
</Border.Background>
<Border.Effect>
<DropShadowEffect ShadowDepth="0"
Color="White"
Opacity="1"
BlurRadius="5"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding HeaderText}"
Grid.Row="0"
Foreground="#5095d6"
FontWeight="Bold"
VerticalAlignment="Bottom"
Margin="{TemplateBinding Margin}"
HorizontalAlignment="Left"/>
<Rectangle Grid.Row="1" Stroke="AntiqueWhite" Margin="1 0"></Rectangle>
<TextBlock Grid.Row="2"
Grid.ColumnSpan="2"
x:Name="PART_TooltipContents"
Margin="5, 2"
Text="{TemplateBinding ContentText}"
TextWrapping="Wrap"
MaxWidth="200"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The use of the control:
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="Button1" Content="Button with popup" HorizontalAlignment="Center">
</Button>
<Button x:Name="Button2" Content="Another button with popup" HorizontalAlignment="Center">
</Button>
<Popup IsOpen="True"
FlowDirection="LeftToRight"
Margin="10"
PlacementTarget="{Binding ElementName=Button1}"
Placement="top"
StaysOpen="True">
<local2:CustomPopupControl HeaderText="Some Header Text" ContentText="Content Text that could be any text needed from a binding or other source" Margin="2">
</local2:CustomPopupControl>
</Popup>
<Popup IsOpen="True"
FlowDirection="LeftToRight"
Margin="10"
PlacementTarget="{Binding ElementName=Button2}"
Placement="Bottom"
StaysOpen="True">
<local2:CustomPopupControl HeaderText="Different header text" ContentText="Some other text" Margin="2">
</local2:CustomPopupControl>
</Popup>
</StackPanel>
I tried illustrating how some properties can be constant across all controls, others can be customized per control and others could be bound to TemplatePart, here is the final result:
Depends how you want your pop-ups to behave. If they're just for displaying information in a uniform manner, than you might want to have a class that derives from Window that has the standard formats and styling wrapped around a ContentPresenter then bind the content of the presenter to a property which can represent the custom information for each pop-up.
Then its just a matter of programatically inserting whatever custom content you want before displaying the pop-up window.
Hope it helps.

In WPF, how do I give my custom control a default style to be used in Design Mode?

I have created a custom WPF control. The control acts as a container with various regions (so it can work like a master page).
The style for this control is loaded at runtime from a separate resource dictionary as follows:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyApp.Application;component/Themes/Theme.xaml" x:Name="theme"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
My custom control's style looks as follows...
<Style TargetType="{x:Type shareduc:EditControlMaster}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type shareduc:EditControlMaster}">
<Grid>
<Grid.ColumnDefinitions></Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Border BorderBrush="{DynamicResource xxBorderBrush}"
BorderThickness="0,1,0,1" Background="White" Grid.Row="0">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Margin="10" Content="{TemplateBinding Image}" />
<ContentPresenter Grid.Row="0" Grid.Column="1" Margin="2" Content="{TemplateBinding Title}" />
<ContentPresenter Grid.Row="1" Grid.Column="1" Margin="2" Content="{TemplateBinding Abstract}" />
</Grid>
</Border>
<ContentPresenter Grid.Row="1" Margin="2" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that this style is only loaded at Runtime. So in Design Mode my control does not have any style and does not have any size or layout. How can I give my control a default style for Design Mode?
Update:
I'm making some progress... it appears I can specify a default theme to use in a file called Themes\Generic.xaml. This works fine in a small sample project, but for some reason my VS2008 designer stays blank when I do the same thing in my actual project... Help? :(
Note that my custom control's code looks as follows:
public partial class EditControlMaster : Control
{
static EditControlMaster()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EditControlMaster),
new FrameworkPropertyMetadata(typeof(EditControlMaster)));
}
public object Title
{
get { return (object)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(object),
typeof(EditControlMaster), new UIPropertyMetadata());
public object Image
{
get { return (object)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(object),
typeof(EditControlMaster), new UIPropertyMetadata());
public object Abstract
{
get { return (object)GetValue(AbstractProperty); }
set { SetValue(AbstractProperty, value); }
}
public static readonly DependencyProperty AbstractProperty =
DependencyProperty.Register("Abstract", typeof(object),
typeof(EditControlMaster), new UIPropertyMetadata());
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object),
typeof(EditControlMaster), new UIPropertyMetadata());
}
Through lots of poking around project files I have figured out what was wrong!
Themes\Generic.xaml contains your control's default Style. This is fine.
Your Assembly.cs file needs to contain the following attribute:
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
Voila! The VS2008 designer works!
Did you try
public EditControlMaster()
{
DefaultStyleKey = typeof(EditControlMaster);
}
as part of the constructor?
Try moving the style to a standard place.
Add / New Item / Custom Control(WPF) / "MyDummyControl"
now place your style in "Themes/Generic.xaml" that was created
remove "MyDummyControl" files and style
remove your Theme.xaml, and MergedDictionaries
One more thing, in my experience, using DynamicResource in a style defined in themes\generic.xaml (like you did for the Border) does not work (at least, does not work always). You should consider changing that to a StaticResource lookup.

Resources