Transfer Content value of a UserControl to a Control inside it - silverlight

I need to extend the bing maps control to be more user MVVM friendly (in specific the ZoomLevel and the BoundingRect properties are not Dependency Properties). I am wrapping the control in a custom usercontrol (I also need to add elements to make other map choices e.g. google maps). I need to transfer the Content Value of the UserControl to the BingMapsControl :
<UserControl x:Class="RevOptWebControls.MVVMMapControl"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
xmlns:mCore="clr-namespace:Microsoft.Maps.MapControl.Core;assembly=Microsoft.Maps.MapControl"
d:DesignHeight="300" d:DesignWidth="400"
x:Name="Root">
<Grid x:Name="LayoutRoot" Background="White">
<m:Map CredentialsProvider="Al_H1LepP6chseYMu31RK76El6k4SUkx2KVrxeqobE3rTXooFPieuEJ6qiuA211I"
CopyrightVisibility="Collapsed"
LogoVisibility="Collapsed"
ScaleVisibility="Visible"
NavigationVisibility="Visible"
x:Name="MyMap">
</m:Map>
<ComboBox x:Name="c_MapTypes" HorizontalAlignment="Right" VerticalAlignment="Top" SelectedIndex="0" Height="30" SelectionChanged="MapTypes_SelectionChanged">
<ComboBoxItem>Google Roads</ComboBoxItem>
<ComboBoxItem>Google Aerial</ComboBoxItem>
<ComboBoxItem>Bing Maps Roads</ComboBoxItem>
<ComboBoxItem>Bing Maps Aerial</ComboBoxItem>
<ComboBoxItem>Open Street Maps</ComboBoxItem>
<ComboBoxItem>Yahoo Street</ComboBoxItem>
<ComboBoxItem>Yahoo Aerial</ComboBoxItem>
<ComboBoxItem>Blank Map</ComboBoxItem>
</ComboBox>
</Grid>
</UserControl>
Update : Figured out how to do it. Shared the control source code as well : http://basaratali.blogspot.com/2010/12/mvvm-version-of-bing-maps-with-google.html

Why not try a Custom Control with custom template. Use
`{TemplateBinding Content}`
to bind Content with Map control.
Example:
<Style TargetType="local:MVVMMapControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MVVMMapControl">
<Grid x:Name="LayoutRoot"
Background="White">
<m:Map CredentialsProvider="Al_H1LepP6chseYMu31RK76El6k4SUkx2KVrxeqobE3rTXooFPieuEJ6qiuA211I"
CopyrightVisibility="Collapsed"
LogoVisibility="Collapsed"
ScaleVisibility="Visible"
NavigationVisibility="Visible"
x:Name="MyMap"
Content="{TemplateBinding ContentControl.Content}"></m:Map>
<ComboBox x:Name="c_MapTypes"
HorizontalAlignment="Right"
VerticalAlignment="Top"
SelectedIndex="0"
Height="30"
SelectionChanged="MapTypes_SelectionChanged">
<ComboBoxItem>Google Roads</ComboBoxItem>
<ComboBoxItem>Google Aerial</ComboBoxItem>
<ComboBoxItem>Bing Maps Roads</ComboBoxItem>
<ComboBoxItem>Bing Maps Aerial</ComboBoxItem>
<ComboBoxItem>Open Street Maps</ComboBoxItem>
<ComboBoxItem>Yahoo Street</ComboBoxItem>
<ComboBoxItem>Yahoo Aerial</ComboBoxItem>
<ComboBoxItem>Blank Map</ComboBoxItem>
</ComboBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Class:
public class MVVMMapControl : ContentControl // Notice this inherits from ContentControl for its Content Property
{
public MVVMMapControl()
{
this.DefaultStyleKey = typeof(MVVMMapControl);
}
}

Related

Capturing name of StackPanel from Drop event

Within WPF I have the following XAML code:
<Page x:Class="com.MyCo.MyProj.Pages.Configuration.ManageLinkage.MasterLinkage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:com.MyCo.MyProj.Pages.Configuration.ManageLinkage"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="MasterLinkage">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TabControl TabStripPlacement="Top" Background="Transparent">
<TabItem Header="Import">
<ListBox Margin="0,5,0,0" Name="lbxImportItems" HorizontalAlignment="Left" VerticalAlignment="Top" Width="110" Background="Transparent"
PreviewMouseLeftButtonDown="lbxImportItems_PreviewMouseLeftButtonDown" >
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Name="DBImport">
<Image Source="/Images/DBImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="Database" Foreground="AntiqueWhite"/>
</StackPanel>
<StackPanel Orientation="Vertical" Name="CSVImport">
<Image Source="/Images/CSVImport25px.png" VerticalAlignment="Center" HorizontalAlignment="Center"></Image>
<TextBlock Text="CSV Import" Foreground="AntiqueWhite"/>
</StackPanel>
</ListBox>
</TabItem>
</TabControl>
<Canvas x:Name="cnvsLinkScreen" AllowDrop="True" Grid.Column="1" Background="Transparent" Drop="cnvsLinkScreen_Drop" DragOver="cnvsLinkScreen_DragOver" ></Canvas>
</Grid>
The code for capturing the event is here:
private void cnvsLinkScreen_Drop(object sender, DragEventArgs e)
{
Canvas parent = (Canvas)sender;
object data = e.Data.GetData(typeof(string));
StackPanel objIn = (StackPanel)e.Data;
...
}
The drag and drop work great, the event method created the image in the canvas. However, I want to capture the Name="" from the StackPanels which are dropped.
I found the Name buried super deep in the "DragEventArgs e" object. I was think that there should be a way to cast the object (or the object within that object) as a StackPanel to easily work with it. The above code does not convert the StackPanel object( it's not at the root or the child object; I tried both) so it exceptions on "StackPanel objIn = (StackPanel)e.data;"
How do I either translate the incoming object to a StackPanel or how do I access the Name attribute from the Stackpanel?
I got it. I was close with the translation. To translate / typecast the object to what you are working with I needed to use the following line:
StackPanel objIn = (StackPanel)(e.Data.GetData(typeof(StackPanel)));
Which is slightly different than above.

Scrollbars not visible + Events not thrown

I would like to implement a userControl which is able to scroll and zoom with provided mouse input.
Therefore, I implemented following user Control. (If its working I am going to move the Events directly to the Model)
ScrollDragZoomControl.xaml
<UserControl x:Class="MinimalMonitoringClient.Controls.ScrollDragZoomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:models="clr-namespace:MinimalMonitoringClient.Controls.Models"
Background="Transparent">
<UserControl.DataContext>
<models:ScrollDragZoomViewModel />
</UserControl.DataContext>
<ScrollViewer x:Name="scrollViewer"
MouseLeftButtonUp="scrollViewer_MouseLeftButtonUp"
PreviewMouseLeftButtonUp="scrollViewer_PreviewMouseLeftButtonUp"
PreviewMouseWheel="scrollViewer_PreviewMouseWheel"
PreviewMouseLeftButtonDown="scrollViewer_PreviewMouseLeftButtonDown"
MouseMove="scrollViewer_MouseMove"
VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility}"
HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility}">
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Grid.LayoutTransform>
<Viewbox>
<!-- Present the actual stuff the user wants to display -->
<ContentPresenter />
</Viewbox>
</Grid>
</ScrollViewer>
In another UserControl I would like to use this UserControl so show Content which could be bigger than the current UserControl's width and height. For Testing I set Width and Height to something huge.
<UserControl x:Class="MinimalMonitoringClient.Panels.HierarchicalView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:panels="clr-namespace:MinimalMonitoringClient.Models.Panels"
xmlns:controls="clr-namespace:MinimalMonitoringClient.Controls">
<UserControl.DataContext>
<panels:HierarchicalViewModel />
</UserControl.DataContext>
<controls:ScrollDragZoomControl>
<Image Source="/Images/Information.png" Width="2000" Height="2000" IsHitTestVisible="False" />
</controls:ScrollDragZoomControl>
No Events are called in the ScrollDragZoomControl. I set the
Background to transparent. Also played with IsHitTestVisible.
The Scrollbars are not visible.
When you set the Content property of the UserControl to an Image element you effectively "override" any content that you have defined in ScrollDragZoomControl.xaml.
You probably want the ScrollViewer and the rest of the stuff you have defined in ScrollDragZoomControl.xaml to be part of the UserControl's template:
<UserControl x:Class="MinimalMonitoringClient.Controls.ScrollDragZoomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:models="clr-namespace:MinimalMonitoringClient.Controls.Models"
Background="Transparent">
<UserControl.DataContext>
<panels:HierarchicalViewModel />
</UserControl.DataContext>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ScrollViewer x:Name="scrollViewer"
MouseLeftButtonUp="scrollViewer_MouseLeftButtonUp"
PreviewMouseLeftButtonUp="scrollViewer_PreviewMouseLeftButtonUp"
PreviewMouseWheel="scrollViewer_PreviewMouseWheel"
PreviewMouseLeftButtonDown="scrollViewer_PreviewMouseLeftButtonDown"
MouseMove="scrollViewer_MouseMove"
VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility}"
HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility}">
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Grid.LayoutTransform>
<Viewbox>
<!-- Present the actual stuff the user wants to display -->
<ContentPresenter />
</Viewbox>
</Grid>
</ScrollViewer>
</ControlTemplate>
</UserControl.Template>
</UserControl>

WPF share resourcedictionaries among controls

I have 2 dictionaries merged in my WPF app (base dict merged with skin dict).
It works very fine on the MainWindow, but when I added a new WPF Window, it seems unable to access the StaticResource.
This is the code of the new Window:
<Window x:Class="Sc2ReplayMonkey.PleaseWaitWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:local="clr-namespace:Sc2ReplayMonkey"
Title="PleaseWaitWindow" Height="300" Width="300">
<Grid Style="{StaticResource WindowBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource WindowTextelement}" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
Please wait while the replays
</TextBlock>
<TextBlock Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
are parsed and serialized
</TextBlock>
</Grid>
</Window>
I get the error "Cannot find the resource named "WindowBackground".
It is defined in the skin dict as:
<Style x:Key="WindowBackground" TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Black" />
</Style>
What did I miss?
I think you need to add
Resources.MergedDictionaries.Add(resources);
in you App.cs class once f.e in Initialize() method. Then your dictionary will be accessible from any windows.
This solved the problem, I simply added a line in PleaseWaitWindow's constructor: Resources = main.Resources; main being the MainWindow

How to add scrolling buttons to a data bound, horiztonal list

So I have a List, which contains items. Right now they are thumbnails of pictures. I wanted this list to be bound to a changing list in code behind, so I used a Listbox. However, I needed this box to flow horizontally. So it is styled as a StackPanel. Lastly, I want buttons to control the scrolling, not scrollbars. That's the part that does not work Here's a code sample :
<UserControl x:Class="TestBench.MainPage"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<Style x:Key="StackHorz" TargetType="ListBox">
<Style.Setters>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Background="AliceBlue" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer BorderBrush="DarkGreen" BorderThickness="2" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="_Next" Content="NEXT" Height="20" Width="40" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
<Button x:Name="_Prev" Content="PREV" Height="20" Width="40" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<ListBox x:Name="TestList" Height="100" Width="800" VerticalAlignment="Top">
...Insert ListItems...
</ListBox>
</Grid>
In this example the listbox is not bound, but I do need to be able to set ItemsSource={Binding Content}. Code behind I tried was :
namespace TestBench
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
TestList.Style = this.Resources["StackHorzTop"] as Style;
_Next.Click += new RoutedEventHandler(_Next_Click);
_Prev.Click += new RoutedEventHandler(_Prev_Click);
}
void _Prev_Click(object sender, RoutedEventArgs e)
{
TestList.ScrollIntoView(TestList.Items[0]);
}
void _Next_Click(object sender, RoutedEventArgs e)
{
TestList.ScrollIntoView(TestList.Items[TestList.Items.Count - 1]);
}
}
}
But the ScrollIntoView does nothing. I also tried getting the ScrollViewer as a VisualTreeHelper.GetChild() of the list box, but scrolling there using ScrollToHorizontalOffset() does nothing either.
I know it's a weird way to set things up, but I need all 3 functionalities (Binding, Horizontal orientation, No scroll bars with button scrolling). Anyone know where I am going wrong on this one?
Thanks in advance,
Chart.
Maybe you can try putting your listbox in a ScrollViewer and style the ScrollViewer so that the scroll bars are not visible (only the buttons).
More info on the ScrollViewer's templatable parts can be found here.

Multi-touch ScrollViewer eating touch events

WPF 4.0:
I have scroll viewer with many Sliders inside of it. I want the scroll viewer to pan with touch, and I want the internal slider to also respond to touch.
Unfortunately, the scroll viewer is eating the "TouchMove" events and not passing them down to the slider control. Any idea how to fix this?
Here is my XAML:
<Window x:Class="ScrollingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto" PanningMode="Both" >
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Height="100" BorderThickness="2" BorderBrush="Black">
<Slider Value="{Binding ., Mode=TwoWay}" Width="300" Minimum="0" Maximum="100" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
And my Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Items;
}
public IEnumerable<int> Items
{
get
{
return Enumerable.Range(0, 50);
}
}
}
Please see my answer to this question: ScrollViewer in a touch interface not working properly
I just resolved this issue in our app as well using a custom Thumb control - in my answer I explain what is causing the problem.
This sounds like a case of "routed event marked as handled". Can you try using AddHandler to subscribe to that event, and set the last parameter "handledEventsToo" to true?
Cheers,
Laurent
It's handling the TouchMove event, most likely. There are bubbling events (PreviewTouchMove, etc) you could handle in your Slider control. You would need to coordinate how you want the touch events to be handled.
You can try to make your custom class, derived from ScrollViewer and override OnTouchMove method.
public class CustomScrollViewer : System.Windows.Controls.ScrollViewer
{
protected override void OnTouchMove(System.Windows.Input.TouchEventArgs e)
{
// delete the base.OnTouchMove() call to prevent event being "eat" :)
}
}
Then, you edit the xaml like this:
<Window x:Class="ScrollingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.Template>
<ControlTemplate>
<local:CustomScrollViewer VerticalScrollBarVisibility="Auto" PanningMode="Both" >
<ItemsPresenter />
</local:CustomScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Height="100" BorderThickness="2" BorderBrush="Black">
<Slider Value="{Binding ., Mode=TwoWay}" Width="300" Minimum="0" Maximum="100" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>

Resources