Bind Element to different DataContext property - wpf

forgive this question - just learning WPF and it's making my brain hurt. Can't get my head around Bindings/DataContexts. XAML below produced via some code monkery (my knowledge of WPF not good enough atm to know what is helpful to post or not):
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:MoveResize" WindowStartupLocation="CenterScreen"
xmlns:paz="clr-namespace:Wpf.Controls.PanAndZoom;assembly=Wpf.Controls.PanAndZoom"
Title="Move and resize" Height="550" Width="750" Loaded="Window_Loaded" KeyDown="Window_KeyDown" KeyUp="Window_KeyUp">
<Window.Resources>
<!-- Custom Thumb Style-->
<Style x:Key="SliderThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Rectangle x:Name="Rectangle" StrokeThickness="1" Stretch="Fill" Opacity="1">
<Rectangle.Stroke>
<SolidColorBrush Color="Black"></SolidColorBrush>
</Rectangle.Stroke>
<Rectangle.Fill>
<SolidColorBrush Color="Yellow"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- MoveThumb Template -->
<ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type s:MoveThumb}">
<Rectangle Fill="Transparent" />
</ControlTemplate>
<!-- ResizeDecorator Template -->
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="{x:Type Control}">
<Grid>
<!-- Abridged for stackoverflow posting -->
<s:ResizeThumb Style="{StaticResource SliderThumbStyle }" Width="9" Height="9" Cursor="SizeNWSE" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
</Grid>
</ControlTemplate>
<!-- Designer Item Template-->
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll" />
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" />
<Control Template="{StaticResource ResizeDecoratorTemplate}" />
</Grid>
</ControlTemplate>
</Window.Resources>
<paz:ZoomBorder x:FieldModifier="public" Name="zoomBorder" Stretch="None" ZoomSpeed="1.2" Background="SlateBlue" ClipToBounds="True" Focusable="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="4" Grid.Column="1">
<Canvas x:Name="workspace" Background="#555" Width="1920" Height="1080" Margin="97,93,-1275,-654">
<Path x:Name="YellowDiamond" Fill="Yellow" Data="M 0,5 5,0 10,5 5,10 Z" Stretch="Fill" IsHitTestVisible="True" Canvas.Left="10" Canvas.Top="10" Height="124.75" Width="134.5" />
<ContentControl Width="130" MinWidth="50" Height="130" MinHeight="50" Canvas.Top="150" Canvas.Left="150" Template="{StaticResource DesignerItemTemplate}">
<Path x:Name="BlueDiamond" Fill="Blue" Data="M 0,5 5,0 10,5 5,10 Z" Stretch="Fill" IsHitTestVisible="False" />
</ContentControl>
<TextBox x:Name="widthTB" Height="23" Canvas.Left="131" TextWrapping="Wrap" Text="{Binding BastardWidth, Mode=OneWay}" Canvas.Top="348" Width="120" />
</Canvas>
</paz:ZoomBorder>
</Window>
The key line is:
<s:ResizeThumb Style="{StaticResource SliderThumbStyle }" Width="9" Height="9" Cursor="SizeNWSE" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
I'm wanting to set Width and Height to a property stored in certain class (PanAndZoomController). My understanding is that this is presently databound to ResizeThumb.vb. The relevant code behind:
Partial Public Class MainWindow
Private _panZoomControl As New PanAndZoomController
Public Property PanZoomController() As PanAndZoomController
Get
Return _panZoomControl
End Get
Set(ByVal value As PanAndZoomController)
_panZoomControl = value
End Set
End Property
Public Sub New()
InitializeComponent()
End Sub
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Me.DataContext = _panZoomControl
End Sub
Private Sub MousewheelMoved(sender As Object, e As MouseWheelEventArgs) Handles Me.MouseWheel
_panZoomControl.ZoomFactor = zoomBorder.ZoomX
End Sub
End Class
Public Class PanAndZoomController
Private _zoomFactor As Double
Public Property ZoomFactor() As Double
Get
Return _zoomFactor
End Get
Set(ByVal value As Double)
_zoomFactor = value
_scaledWidth = 10 * _zoomFactor
End Set
End Property
Private _scaledWidth As Double = 10
Public Property ScaledWidth() As Double
Get
Return _scaledWidth
End Get
Set(ByVal value As Double)
_scaledWidth = value
End Set
End Property
End Class
Thus, Width and Height should be populated from MainWindow.PanZoomController.ScaledWidth. Could someone help out with how to achieve this? In as little XAML as possible, please!? I still need to preserve the binding of that element to s:ResizeThumb due to the code behind.
EDIT: In response to ASh's suggestions.
I've tried NotifyPropertyChanged with what I think is the right way, but still no joy :(
Simplified code scenario. Relevant code behind:
Public Class Workspace
Implements INotifyPropertyChanged
Public Sub NotifyPropertyChanged(ByVal propName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _canvasZoomFactor As Double = 1
Public Property CanvasZoomFactor() As Double
Get
Return _canvasZoomFactor
End Get
Set(ByVal value As Double)
_canvasZoomFactor = value
_handleWidth = 5 * value
Me.NotifyPropertyChanged("HandleWidth")
End Set
End Property
Private _handleWidth As Double = 5
Public ReadOnly Property HandleWidth() As Double
Get
Return _handleWidth
End Get
End Property
End Class
XAML:
<UserControl x:Name="MainWorkspace" x:Class="Workspace"
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:s="clr-namespace:stigzler.utility.WPFcontrols"
xmlns:local="clr-namespace:stigzler.utility.WPFcontrols"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="{x:Type Control}">
<Grid SnapsToDevicePixels="false">
<s:ResizeThumb Width="{Binding ElementName=Workspace, Path=HandleWidth,UpdateSourceTrigger=PropertyChanged}"
Height="{Binding ElementName=Workspace, Path=HandleWidth,UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource SliderThumbStyle}" Cursor="SizeNWSE" VerticalAlignment="Top"
HorizontalAlignment="Left" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" />
<Control Template="{StaticResource ResizeDecoratorTemplate}" />
</Grid>
</ControlTemplate>
<ContentControl Width="130" MinWidth="50" Height="130" MinHeight="50" Canvas.Top="150" Canvas.Left="150" Template="{StaticResource DesignerItemTemplate}">
<Path x:Name="BlueDiamond" Fill="Blue" Data="M 0,5 5,0 10,5 5,10 Z" Stretch="Fill" IsHitTestVisible="False" />
</ContentControl>
</UserControl>
Relevant lines:
<s:ResizeThumb Width="{Binding ElementName=Workspace, Path=HandleWidth,UpdateSourceTrigger=PropertyChanged}"
Me.NotifyPropertyChanged("HandleWidth")
Still getting no joy. Any other suggestions (I'm still really struggling to get my head around this binding lark).

"My understanding is that this is presently databound to ResizeThumb.vb" - there can't be a binding to a file with code.
Each binding must have a source to get values. There are some options to provide that source:
specify it directly, e.g.: "{Binding Source={StaticResource}}";
use DataContext - the default option: "{Binding Path=SomeProperty}" - when no source is provided, then the binding will try to find requested property in a DataContext - some data associated with the View (or with part of the View - one might want to work with different data in different parts). Usually there is one DataContext per view (Me.DataContext = _panZoomControl)
use other element as binding source (via RelativeSource or ElementName), if the element are in the same view.
s:ResizeThumb is inside MainWindow view, so RelativeSource should work:
<s:ResizeThumb Style="{StaticResource SliderThumbStyle}"
Width="{Binding Path=PanZoomController.ScaledWidth, RelativeSource={RelativeSource AncestorType = {x:Type s:MainWindow}}}"
Height="{Binding Path=PanZoomController.ScaledWidth, RelativeSource={RelativeSource AncestorType = {x:Type s:MainWindow}}}"
hint: when binding doesn't work for some reason, check Output window in Visual Studio - there can be a message describing binding problem (type mismatch, missing property, incorrect DataContext are common reasons)

In addition to #ASh's answer (+1) on how to actually bind to the properties, you should also implement the INotifyPropertyChanged interface in your PanAndZoomController class in order for the view to be refreshed whenever you set the ZoomFactor and ScaledWidth properties dynamically:
Public Class PanAndZoomController
Implements INotifyPropertyChanged
Private _zoomFactor As Double
Public Property ZoomFactor() As Double
Get
Return _zoomFactor
End Get
Set(ByVal value As Double)
_zoomFactor = value
NotifyPropertyChanged(NameOf(ZoomFactor))
ScaledWidth = 10 * _zoomFactor
End Set
End Property
Private _scaledWidth As Double = 10
Public Property ScaledWidth() As Double
Get
Return _scaledWidth
End Get
Set(ByVal value As Double)
_scaledWidth = value
NotifyPropertyChanged(NameOf(ScaledWidth))
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub NotifyPropertyChanged(info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
End Class

Related

Binding collection to ComboBox in Listview

I have a ListView whose DataTemplate consists of a ComboBox and some TextBoxes.
The ComboBox is bound to a Collection which is mapped to a CollectionViewSource.
The ListView can have any number of rows.
The problem is that selecting an item in one ComboBox changes them all. I do want them all to be populated with the same contents, but to be able to be set independently.
The Resources section contains the following:
<CollectionViewSource Source="{Binding ChildAccounts}" x:Key="ChildGroupedData">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="group"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!-- Template for each child item in ListView -->
<DataTemplate x:Key="ChildTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="210"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Account" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="{StaticResource CustomWhite}" FontSize="14" Width="80"/>
<ComboBox Grid.Column="1" SelectedValue="{Binding Path=accFrom}" ItemsSource="{Binding Source={StaticResource ChildGroupedData}}" ItemTemplate="{StaticResource AccountTemplate}" SelectedValuePath="ID" Width="120" Style="{StaticResource RoundedComboBox}" HorizontalAlignment="Left" VerticalAlignment="Center">
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource CustomExpanderComboGroupItemStyle}" HeaderTemplate="{StaticResource GroupHeader}"/>
</ComboBox.GroupStyle>
</ComboBox>
<Label Grid.Column="2" Content="Amount" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="{StaticResource CustomWhite}" FontSize="14" Width="80"/>
<TextBox Grid.Column="3" Text="{Binding Path=amount, StringFormat='#,##0.00'}" Style="{StaticResource RoundedTextBox}" Width="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<Label Grid.Column="4" Content="Comment" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="{StaticResource CustomWhite}" FontSize="14" Width="80"/>
<TextBox Grid.Column="5" Text="{Binding Path=comment}" Style="{StaticResource RoundedTextBox}" Width="200" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
<!-- ListView template -->
<Style x:Key="ChildListViewStyle" TargetType="{x:Type ListView}">
<Setter Property="ItemTemplate" Value="{DynamicResource ChildTemplate}"/>
<Setter Property="Background" Value="{StaticResource CustomBackground}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Margin" Value="10,10,10,10"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="0,0,50,0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Style.Resources>
<!-- Makes selection invisible when focus lost -->
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{StaticResource CustomBackgroundC}"/>
</Style.Resources>
</Style>
The ListView is defined as:
<ListView Grid.Column="0" x:Name="lstChildren" Margin="20,30,0,0" ItemsSource="{Binding Path=Items}" Style="{StaticResource ChildListViewStyle}"/>
EDIT:
The following is found in the associated class
Imports System.Data
Imports System.Data.OleDb
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Class ItemView
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
....
Private _ChildAccounts As ObservableCollection(Of AccountEntry)
Public Property ChildAccounts As ObservableCollection(Of AccountEntry)
Get
Return _ChildAccounts
End Get
Set(value As ObservableCollection(Of AccountEntry))
_ChildAccounts = value
End Set
End Property
....
Private Sub ItemView_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
Dim dsacmd As OleDbDataAdapter
Dim dsa As New DataSet
Dim dva As DataView
Dim strSelect As String
Try
' ** Open a connection to the database.
cn = New OleDbConnection(strConnection)
cn.Open()
Me.DataContext = Me
strSelect = "SELECT Accounts.ID, Accounts.deleted, Accounts.accType, Accounts.currency as curr, IIf([Currencies.ID]=1,Accounts.comment,Accounts.comment & "" ("" & Currencies.symbol & "")"") AS comment, Currencies.comment AS currS FROM Currencies INNER JOIN Accounts ON Currencies.ID = Accounts.currency"
dsacmd = New OleDbDataAdapter(strSelect, cn)
dsacmd.Fill(dsa, "Accounts")
dva = New DataView(dsa.Tables("Accounts"))
dva.RowFilter = "accType=" & cVirtual.ToString & " AND deleted=False"
dva.Sort = "curr, comment"
ChildAccounts = New ObservableCollection(Of AccountEntry)(dva.ToTable.AsEnumerable().[Select](Function(i) New [AccountEntry](i("ID"), i("currS").TrimEnd(" "), i("comment"))))
....
Private Sub DisplayItem()
....
strSelect = ""
Dim Relations As Collection(Of Relation) = GetRelations(ID)
For Each r As Relation In Relations
strSelect &= "ID=" & r.ID.ToString & " OR "
Next
If strSelect <> "" Then strSelect = "SELECT * FROM Items WHERE " & strSelect.Substring(0, strSelect.Length - 4)
If strSelect <> "" Then
dsrcmd = New OleDbDataAdapter(strSelect, cn)
dsr.Clear()
dsrcmd.Fill(dsr, "Items")
lstChildren.DataContext = dsr
End If
....
You will need to move your CollectionViewSource into the DataTemplate. To force all combobox-items to use the same Source Collection I think you can try two things:
One - Use Relative Source to pick the ChildAccounts from ListView's DataContext
...
<Grid.Resources>
<CollectionViewSource x:Key="ChildGroupedData"
Source="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}},
Path=DataContext.ChildAccounts}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="group" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
...
Update:
Above method can be seen in This sample I put together. Note that ChildAccount's is just an ObservableCollection<T> in the view-model, this is so that we are not having a new collection per ListBoxItem but share em across like what you had asked for in the question.
If your ChildAccounts property is actually like say the "comment" property where you have a ChildAccounts property object per ListBoxItem, just change
<CollectionViewSource x:Key="ChildGroupedData"
Source="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListView}},
Path=DataContext.ChildAccounts}">
to
<CollectionViewSource x:Key="ChildGroupedData"
Source="{Binding ChildAccounts}">
and it should work as well. You do however now have a ChildAccounts collection per ListBoxItem and aren't sharing their source.
Main thing is to Define the CollectionViewSource inside the DataTemplate. Download the attached sample and try it for yourself and check the specifics.
Two - Have ChildAccounts as a static variable
...
<Grid.Resources>
<CollectionViewSource x:Key="ChildGroupedData"
Source="{x:Static local:ChildAccounts}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="group" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
...
You can add set IsSynchronizedWithCurrentItem="False" to the combo box.

Silverlight ZOrder doesn' work for me in Bing control

I'm trying to make my popup widget to be on top in a map but setting Canvas.ZOrder doesn't help.
Here is XAML:
<m:Map x:Name="MainMap"
Margin="0,6,3,3"
ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}"
Center="{Binding MapCenter, Mode=TwoWay}"
CopyrightVisibility="Collapsed"
CredentialsProvider="{Binding BingCredentialsProvider}"
UseInertia="True"
Mode="Road" Grid.Column="2" Grid.Row="1">
<m:MapItemsControl ItemsSource="{Binding Source={StaticResource WorkLayerData}}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<Canvas
m:MapLayer.Position="{Binding Location}">
<Button
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}"
Margin="{Binding PushpinMargin}"
Style="{StaticResource LooklessButtonStyle}"
Command="{Binding DataContext.SelectedPushpinChangedCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"
Cursor="Hand">
<Ellipse
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}" Stroke="Black" Fill="{Binding IsGPSDataRecent, Converter={StaticResource BoolToGreenRedBrushConverter}}" StrokeThickness="1">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding DeviceId}" />
</ToolTipService.ToolTip>
</Ellipse>
</Button>
<!-- Show black dot over actual GPS point -->
<Ellipse
Width="10" Height="10" Stroke="Black" Fill="Black" StrokeThickness="1"
Margin="-5,-5,0,0"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" />
<Border
Width="200"
BorderThickness="1" BorderBrush="DarkGray"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Color="#FF000000" Opacity="0.5" ShadowDepth="2" />
</Border.Effect>
<ContentControl Template="{StaticResource TrackedAssetControlTemplate}" />
</Border>
</Canvas>
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:Map>
I tried to set ZIndex on a border but no luck.
Here is how it looks when IsSelected = true (see other dots with ZIndex higher on top)
In order to bring an item in a MapItemsControl to front it is necessary to set the ZIndex of the item container. You can do that in code behind by retrieving the item container from the MapItemsControl's ItemContainerGenerator.
In case you don't want that, you could apply an attached helper property to the top-level container (the Canvas) in the DataTemplate for your map items. As this Canvas is the direct child of the item container, the helper property would have to set the ZIndex of the visual parent of the Canvas. If that sounds weird, here is the code for the attached property, in a helper class called MapItem:
public class MapItem
{
public static readonly DependencyProperty ZIndexProperty =
DependencyProperty.RegisterAttached("ZIndex", typeof(int),
typeof(MapItem), new PropertyMetadata(ZIndexChanged));
public static int GetZIndex(DependencyObject obj)
{
return (int)obj.GetValue(ZIndexProperty);
}
public static void SetZIndex(DependencyObject obj, int value)
{
obj.SetValue(ZIndexProperty, value);
}
private static void ZIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// set ZIndex on parent of obj
Canvas.SetZIndex((UIElement)VisualTreeHelper.GetParent(obj), (int)e.NewValue);
}
}
In your DataTemplate you may now bind the helper property to one of your VM properties, perhaps by using an appropriate binding converter:
<DataTemplate x:Key="MapItemDataTemplate">
<!-- setting the helper property MapItem.ZIndex on Canvas
sets the Canvas.ZIndex property on the item container -->
<Canvas local:MapItem.ZIndex="{Binding ...}">
...
</Canvas>
</DataTemplate>

WPF:change background color of listview item

how to change the foreground(even Background will do) of item in listview.
i have a program which opens excel and check for broken links in the given sheet.
i want to display the user the list of given links and whcih ever is broken. i want to change foreground to RED.
i have bound the values to observablecollection. the collection is just collection of strings(WEB LINKS) and is working fine
should i do multi binding like here
EDIT:-
have got one more problem on this. after creating the property and making all the changes. the WPF form is not getting updated with the values. i.e. the list box is not getting filled. neither the coloring is happening( not sure it is happening also. as i cant see them).
here is my code. please excuse my coding i am new to WPF
<Window x:Class="URLValide.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Local="clr-namespace:URL_Validater.URLValide"
Title="URL Validator" Height="269" Width="525" FontSize="13"
ResizeMode="NoResize">
<Window.Resources>
<ResourceDictionary>
<Style x:Key="HighLight" TargetType="{x:Type Control}">
<Setter Property="Background" Value="Honeydew"/>
<Setter Property="FontWeight" Value="UltraBlack"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="40" Color="Beige" Direction="50" Opacity="0.5"/>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Window.Resources>
<DockPanel LastChildFill="True">
<Label DockPanel.Dock="Top" Name="WinHeader" Height="30" Background="BurlyWood" FontFamily="Calibri" FontSize="20" FontWeight="Bold" FontStretch="Medium" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,0,0,0" HorizontalContentAlignment="Center" VerticalContentAlignment="Top">URL Validator</Label>
<Grid DockPanel.Dock="Bottom" Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" MaxHeight="30"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Text="Current URL:"></TextBlock>
<TextBlock Grid.Column="1" Text="{Binding strCrnUrl}" Width="370"></TextBlock>
<TextBlock Grid.Column="2" Text="10 of 100" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBlock>
</Grid>
<Grid DockPanel.Dock="Right" Width="154">
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="300" Width="19*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="55" MinHeight="10" Height="35*" />
<RowDefinition Height="165*" MinHeight="10" />
</Grid.RowDefinitions>
<Label Name="LbleRight" BorderBrush="Black" BorderThickness="1" Margin="0,2" FontSize="13" Content="List Of URL's" Height="30" Grid.ColumnSpan="2" Grid.Row="0"></Label>
<ListView Grid.Row="1" Background="Azure" ItemsSource="{Binding strPdfLst}" IsEnabled="True" Margin="0,0,0,1"></ListView>
</Grid>
<Grid DockPanel.Dock="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30*" />
<RowDefinition Height="60*" />
</Grid.RowDefinitions>
<Label BorderBrush="Black" Name="lblFleSel" BorderThickness="1" Height="27" Margin="2,2,0,30" HorizontalAlignment="Left" Width="349">Select File To which Contains the list of URL's:</Label>
<TextBox Name="txtbxFleNme" BorderBrush="Black" BorderThickness="1" Margin="2,34,0,0" Style="{StaticResource ResourceKey=HighLight}" HorizontalAlignment="Left" Width="272"></TextBox>
<Button Name="btnFleSlec" Width="69" Height="27" Content="Browse" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="276,32,4,0"></Button>
<Grid Grid.Row="1" Height="118" HorizontalAlignment="Left" Margin="0,1,0,0" Name="OptionGrid" VerticalAlignment="Top" Grid.RowSpan="2">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1" Height="117" Margin="3,1,0,0" HorizontalAlignment="Stretch" Width="350">
<!--<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Width="162" Height="118">
</StackPanel>-->
<Grid Height="auto" HorizontalAlignment="Stretch" Width="335">
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<CheckBox Name="chkboxHas" VerticalAlignment="Center" HorizontalAlignment="Center">Has Column Headers</CheckBox>
<Label Name="lblDesc" Height="auto" Margin="0,0,204,0" Grid.Row="1" HorizontalContentAlignment="Right" VerticalAlignment="Top">Description Column:</Label>
<ComboBox Grid.Row="1" Name="cmboxDescol" Margin="131,0,0,0" Style="{StaticResource ResourceKey=HighLight}"></ComboBox>
<Label Name="lblUrlCol" Height="auto" Grid.Row="2" Margin="0,0,204,0" HorizontalContentAlignment="Right">URL's Column:</Label>
<ComboBox Name="cmboxUrlCol" Grid.Row="2" Margin="131,0,0,0" Style="{StaticResource ResourceKey=HighLight}"></ComboBox>
<Label Name="lblResCol" Height="auto" Grid.Row="3" Margin="0,0,196,0" HorizontalContentAlignment="Right">Result Column:</Label>
<ComboBox Name="cmboxResCol" Grid.Row="3" Margin="131,0,0,0" Style="{StaticResource ResourceKey=HighLight}"></ComboBox>
<Button Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Validate URL links" Name="btnValidate"></Button>
</Grid>
</Border>
</StackPanel>
</Grid>
</Grid>
</DockPanel>
i am not sure if i should be posting full code. but here is my class
the button click will start the program.
My class code:-
Imports Microsoft
Imports System
Imports System.Net
Imports System.Data
Imports System.Windows
Imports System.Windows.Forms
Imports Excel = Microsoft.Office.Interop.Excel
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Namespace URLValide
Public Class clsUrlCheck
Implements INotifyPropertyChanged
Public Event propertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public exclApplic As New Excel.Application
Public exclWkbOpe As Excel.Workbook
Public exclWksAct As Excel.Worksheet
Public exclRngUrl As Excel.Range
Public exclRngDes As Excel.Range
Public exclRngOut As Excel.Range
Public intMaxRow As Long
Public intCrtRow As Long
Private _strTotlOf As String
Private _strCrnUrl As String
Private _ColorIndx As String
Private _strPdfLst As ObservableCollection(Of String)
Private _strhdrPdf As ObservableCollection(Of String)
#Region "Region of poperties"
Public Property strTotlOf() As String
Get
Return _strTotlOf
End Get
Set(ByVal value As String)
_strTotlOf = value
Me.OnPropertyChanged("strTotlOf")
End Set
End Property
Public Property strCrnUrl() As String
Get
Return _strCrnUrl
End Get
Set(ByVal value As String)
_strCrnUrl = value
Me.OnPropertyChanged("strCrnUrl")
End Set
End Property
Public Property strPdfLst As ObservableCollection(Of String)
Get
Return _strPdfLst
End Get
Set(ByVal value As ObservableCollection(Of String))
_strPdfLst = value
Me.OnPropertyChanged("strPdfLst")
End Set
End Property
Public Property strhdrPdf As ObservableCollection(Of String)
Get
Return _strhdrPdf
End Get
Set(ByVal value As ObservableCollection(Of String))
_strhdrPdf = value
Me.OnPropertyChanged("strhdrPdf")
End Set
End Property
Public Property ColorIndx As String
Get
Return _ColorIndx
End Get
Set(ByVal value As String)
_ColorIndx = value
Me.OnPropertyChanged("ColorIndx")
End Set
End Property
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent propertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
#Region "function of Validating the URL's"
Public Function UrlCheck(ByVal strUrlReq As String)
Try
Dim WebReq As Net.HttpWebRequest = Net.HttpWebRequest.Create(strUrlReq)
WebReq.Method = "Head"
WebReq.Timeout = 5000
Using Response = WebReq.GetResponse()
Return True
End Using
Catch ex As Exception
ColorIndx = "True"
Return False
End Try
End Function
#End Region
#Region "Start Function"
Sub suStart()
Dim blnValid As Boolean
'strPdfLst.Clear()
'strPdfLst = makeList(exclRngUrl.Column, False)
If exclRngDes.Value = "Make Desc" Then
strPdfLst = makeList(exclRngUrl.Column, True)
exclRngDes.Delete()
Else
strPdfLst = makeList(exclRngUrl.Column, False)
End If
For Each exclRngEch As Excel.Range In exclRngUrl
strCrnUrl = exclRngEch.Text
strTotlOf = exclRngEch.Row & "OF" & intMaxRow
blnValid = UrlCheck(exclRngEch.Value.ToString)
If blnValid Then
exclWksAct.Cells(exclRngEch.Row, exclRngOut.Column).value = "Web Page Present"
Else
exclWksAct.Cells(exclRngEch.Row, exclRngOut.Column).value = "Web Page Error"
End If
Next
End Sub
Function makeList(ByVal intColNum As Long, ByVal blnMkeStr As Boolean) As ObservableCollection(Of String)
Dim ObsColTem As ObservableCollection(Of String) = New ObservableCollection(Of String)
If Not blnMkeStr Then
For intLopCnt As Int32 = intCrtRow To intMaxRow
ObsColTem.Add(exclWksAct.Cells(intLopCnt, intColNum).Text)
Next
Else
For intLopCnt As Int32 = intCrtRow To intMaxRow
Dim strDesc As String = Mid(exclWksAct.Cells(intLopCnt, intColNum).Text, InStrRev(exclWksAct.Cells(intLopCnt, intColNum).Text, "/", , CompareMethod.Text) + 1)
ObsColTem.Add(strDesc)
Next
End If
Return ObsColTem
End Function
#End Region
End Class
End Namespace
This is from working code
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=RowSelected}" Value="True">
<Setter Property="Background" Value="Gainsboro" />
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
create a converter which takes the link as parameter and send the background/foreground color, the converters checks the link and sends the correct code accordingly,bind the color code to the itemtemplate background/foreground property.
First create a Broken-Property which you can bind to and then just create a Style for ListViewItems with a DataTrigger on the Broken-Property!
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Broken}" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
Have not tested this code but it should work!

Change user control appearance based on state

I have a user control that consists of four overlapping items: 2 rectangles, an ellipse and a lable
<UserControl x:Class="UserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="50.1" Height="45.424" Background="Transparent" FontSize="24">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3.303*" />
<RowDefinition Height="40*" />
<RowDefinition Height="2.121*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5.344*" />
<ColumnDefinition Width="40.075*" />
<ColumnDefinition Width="4.663*" />
</Grid.ColumnDefinitions>
<Rectangle Name="Rectangle1" RadiusX="5" RadiusY="5" Fill="DarkGray" Grid.ColumnSpan="3" Grid.RowSpan="3" />
<Ellipse Name="ellipse1" Fill="{Binding State}" Margin="0.016,0.001,4.663,0" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Stroke="Black" IsEnabled="True" Panel.ZIndex="2" />
<Label Name="lblNumber" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="White" FontWeight="Bold" FontSize="24" Grid.Column="1" Grid.Row="1" Padding="0" Panel.ZIndex="3">9</Label>
<Rectangle Grid.Column="1" Grid.Row="1" Margin="0.091,0,4.663,0" Fill="Blue" Name="rectangle2" Stroke="Black" Grid.ColumnSpan="2" Panel.ZIndex="1" />
</Grid>
Here is my business object that I want to control the state of my user control:
Imports System.Data
Imports System.ComponentModel
Public Class BusinessObject
Implements INotifyPropertyChanged
Public logger As log4net.ILog
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _state As States
Public Enum States
State1
State2
State3
End Enum
Public Property State() As States
Get
Return _state
End Get
Set(ByVal value As States)
If (value <> _state) Then
_state = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("State"))
End If
End Set
End Property
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
I want to be able to change the state of a business object in the code behind and have that change the colors of multiple shapes in my usercontrol. I'm not sure about how to do the binding. I set the datacontext of the user control in the code behind but not sure if that's right. I'm new to WPF and programming in general and I'm stuck on where to go from here. Any recommendations would be greatly appreciated!!
A simple way would be to use a value converter. Basically this is a class that allows you to bind on a value in your BusinessObject, and depending on what the value is, you return a different brush.
Here is an example showing you exactly how to do it.
[ValueConversion(typeof(States), typeof(Brush))]
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
/* return a different brush depending on the state */
}
}
Then you bind it like this:
<Ellipse Fill="{Binding State, Converter={StaticResource colorConverter} />
Look at the link I provided above to see the full example.
The advantage of this way over Rachel's answer is that it is a generic implementation, so you don't have to create a template for each of your shapes if you want to have this apply to different objects (rectangle, ellipse, etc...). But Rachel's answer - i.e. using a template, is nice too, because it doesn't require any code.
You would create a trigger based on State property, and if it is equal to StateX you would change the color. For example:
<Rectangle>
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State} "
Value="{x:Static localNamespace:States.State1}">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
The localNamespace you'd have to define yourself in the <UserControl> tag. Something like <UserControl xmlns:localNamespace="clr-namespace:MyNamespace.MyClassWithStateEnum;assembly=MyNamespace"

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.

Resources