GMap.NET C# WFP: Can i add custom UserControl into GMapControl overlays? - c#

Project: c#, wpf, .net4.0, Gmap.Net.Presentation 1.7.1.
What I have:
My custom MapControl class inherited from GMap.NET.WindowsPresentation.GMapControl class.
public sealed class MapControl : GMapControl
{
/* Some special data. */
public MapConrol()
: base()
{
/* Some init actions. */
}
/* Overrided and additional methods. */
}
And, for example, I have some custom UserControl class.
Code:
public sealed partial class MapObjectMarkerUiControl : UserControl
{
/* Some special data. */
public MapObjectMarkerUiControl()
{
/* Some init actions. */
}
/* Overrided and additional methods. */
}
Xaml:
<UserControl x:Class="MapCustomControls.MapObjectMarkerUiControl"
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"
mc:Ignorable="d"
Width="40" Height="40" RenderTransformOrigin="0.5, 0.5">
<Grid>
<!-- Some visual controls: text, buttons, etc. -->
</Grid>
</UserControl>
Example of custom user control:
What I need:
Is there any way to add it to the map with reference to the geo coordinates? Something like this one:
gmap.AddCustomUserControl(UserControl customMarker, double latitude, double longitude);
May be I should to inherit my UserControl from other class or implement some Gmap.NET interface that allow to add my widget to the map.
Any advice, tips, help?
P.S. If I solve this problem, I will post it here. I guess it will very helpful for others.
Also, I found a lot of questions about GMap on StackOverflow and so on, and everywhere I saw Overlay class.
GMapOverlay markersOverlay = new GMapOverlay("markers");
gmap.Overlays.Add(markersOverlay);
In my verstion I haven't this one. I have already exist built-in marker overlay into GMap class.
gmap.Markers - ObservableCollection of the GMapMarkers.
And there is no way to create my own overlays and add it to GMapControl object.
UPDATE 0:
First idea in my head. Just add GMapMarkers on the map with some special tag by id of map object, for example. And OnRender() of the GMapControl find all the markers on the screen, parse their ids and draw above my wpf UserControls. But I hope there is some internal mechanics in the GMapControl for that.

I believe the solution is easier than what you've tried, you can derive from GMapMarker, e.g.:
class CustomMarker: GMapMarker
{
public string Description { get; set; }
public CustomMarker(PointLatLng pos, string description)
: base(pos)
{
Description = description;
Shape = new CustomMarkerElement();
((CustomMarkerElement)Shape).lblDesc.Content = description;
}
}
This marker instance gives you access to the UI properties within an own CustomerMarkerElement (an UserControl within the project):
<UserControl x:Class="WpfApplication3.CustomMarkerElement"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Ellipse
Fill="DarkKhaki"
Height="40"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="40" />
<Label
x:Name="lblDesc"
Content="TST"
Foreground="White"
FontWeight="Bold"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
Margin="0,6"
VerticalAlignment="Top"
Width="40"/>
</Grid>
</UserControl>
The downside is that afaik there is no way to have this in an MVVM conform way (e.g. define the custom markers within an item template).

Related

WPF Child Control Inheritance

I am trying to implement a control to inherit from in WPF.
I have never been working with WPF (at least at that level though).
So I need some direction of best practice on how to solve this.
The problem I´m facing is that my control, that I want to inherit from, has some child controls that need to be accessed inside the controls base class.
I want to reuse that control with these child controls inside, because it has functions to fill the child controls from outside.
But since WPF can´t inherit a control with xaml, I can´t get my head around a solution.
Let´s say I have this control.
<StackPanel x:Class="Framework.UI.Controls.Base.Navigator.NavigatorItem"
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:Framework.UI.Controls.Base.Navigator"
mc:Ignorable="d"
d:DesignHeight="26" d:DesignWidth="200">
<Button Name="btnHeader" Content="Button"/>
<TreeView Name="tvNavContent" Height="0"/>
</StackPanel>
In codebehind the Button is being used for a Click event as well as the header Text, which I want to be filled from the Control that inherits from this.
And with a function the TreeView "tvNavContent" is being filled with something like this:
<TreeViewItem x:Class="Framework.UI.Controls.Base.Navigator.NavigatorEntry"
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:Framework.UI.Controls.Base.Navigator"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="200">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Name="imgIcon" Width="16" Height="16" Stretch="Fill"/>
<TextBlock Name="txtTitle"/>
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
What I want to achieve is to reuse the Stackpanel with the Button and TreeView inside and with it´s functions.
I tried two things:
First I tried to create a template and applied that to the base class. After that I just tried to load the controls of the template in the base class with the FindName<>() function.
The problem here is, that the template is applied after InitializeComponent().
But during InitializeComponent() I already need access, to set the controls header property for the title from the control that inherits from the base class.
After that I tried to implement the child controls completely in the base class of the control.
Just created them in the constructor and added them to the Children Property of the stackpanel the base class inherits from.
That did (somewhat) work.
But apparently the controls behave completely different when created like that.
No matter the settings. I just couldn´t get the controls to fit correctly inside their parents.
Furthermore, this method is completely unsuitable for a larger project, when it comes to theme adjustments.
Can someone guide me in the correct direction here?
Create a class called NavigatorItem (without any .xaml file):
public class NavigatorItem : Control
{
static NavigatorItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NavigatorItem),
new FrameworkPropertyMetadata(typeof(NavigatorItem)));
}
}
Create a ResourceDictionary called generic.xaml and put it in a folder called themes (these names are by convention) at the root of your project, and define a default template for the NavigatorItem class in there:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp12">
<Style TargetType="local:NavigatorItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NavigatorItem">
<StackPanel>
<Button Name="btnHeader" Content="Button"/>
<TreeView Name="tvNavContent" Height="0"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
You can then override the OnApplyTemplate of the NavigatorItem class to get a reference to the elements in the template and hook up event handlers to them, e.g.:
public override void OnApplyTemplate()
{
Button button = GetTemplateChild("btnHeader") as Button;
button.Click += Button_Click;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("button clicked!");
}

creating a common structure(theme) in WPF window

I just created a windows Form application to inherit controls from base form and it works fine.
In WPF XAML is it possible to inherit controls from a base form to another like above?
When I tried in visual studio, I have got an error showing:"'parentchild.MainWindow' cannot be the root of a XAML file because it was defined using XAML".
My Basewindow cs code:
namespace parentchild
{
public partial class BaseWindow : Window
{
public BaseWindow()
{
InitializeComponent();
}
}
}
My Basewindow xaml code:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon" x:Class="parentchild.BaseWindow"
Title="BaseWindow" Height="350" Width="525">
<Grid>
<StatusBar HorizontalAlignment="Left" Height="35" Margin="0,285,0,0" VerticalAlignment="Top" Width="517">
<Label Content="Label"/>
<Label Content="Label"/>
</StatusBar>
</Grid>
</Window>
My childwindow cs code:
namespace parentchild
{
public partial class childwindow : BaseWindow
{
public childwindow()
{
InitializeComponent();
}
}
}
My childwindow xaml code:
<mn:BaseWindow x:Class="parentchild.childwindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mn="clr-namespace:parentchild"
Title="childwindow" Height="300" Width="300">
<Grid>
</Grid>
</mn:BaseWindow>
I found another solution by creating an user control and applying it to all windows.Is that the right way?
Or anybody have solution for creating a general theme/structure for all Xaml windows.
Please provide me a solution to solve this issue.
You cannot inherit from a class that has been defined in xaml. I would probably go with creating a UserControl and using that as a 'base container' in any Window you want to have the status bar. If you are intent on making a base Window you could try something like this:
Define the base Window in code only:
public class MyWindowBase : Window
{
private ContentControl contentControl;
public MyWindowBase()
{
this.CreateContent();
}
public Object BaseContent
{
get { return this.contentControl.Content; }
set { this.contentControl.Content = value; }
}
private void CreateContent()
{
var grid = new Grid();
var row1 = new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) };
var row2 = new RowDefinition() { Height = GridLength.Auto };
grid.RowDefinitions.Add(row1);
grid.RowDefinitions.Add(row2);
var statusBar = new StatusBar() { Height = 35, Background = Brushes.Blue }; // Initialize the status bar how you want.
Grid.SetRow(statusBar, 1);
this.contentControl = new ContentControl();
grid.Children.Add(this.contentControl);
grid.Children.Add(statusBar);
base.Content = grid;
}
}
Use the base window in xaml like this:
<WpfApplication7:MyWindowBase xmlns:WpfApplication7="clr-namespace:WpfApplication7" x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="500">
<WpfApplication7:MyWindowBase.BaseContent>
<Button>
Something
</Button>
</WpfApplication7:MyWindowBase.BaseContent>
</WpfApplication7:MyWindowBase>
Of course the base class has room for improvement, like making BaseContent a dependency property, but I think it demonstrates the main idea.
It looks like you are basically trying to create a custom control that extends Window. Creating custom controls in WPF is a complex topic because there is a lot to consider when deciding how to implement one. Take a look at this article that talks about this:
Control Authoring Overview
Most likely, you do not actually want a custom control, but rather a custom ControlTemplate (authored in xaml) that redefines how you want your window to look. You can define the template in a Style and apply that style to any Window that you want. It is a lot to try to explain here in an answer, so you should read up on how control templates work and how they are useful.
If you decide you need to add new properties to your custom window, then you will want to make a new class that extends Window and adds those properties as dependency properties (now you have a custom control). You can then use those properties in your custom ControlTemplate to do whatever you want them to do.
Working in WPF is not at all like working in Windows Forms. If you try to apply techniques and practices you learned in Windows Forms to a WPF application, you will cause yourself a lot of headaches. I made that transition a couple years ago, and my advice is to make no assumptions about WPF based on what you know from Windows Forms. They are completely different systems with different ways of doing things.

InitializeComponent "doesn't exist" and "no definition" for XAML map controls

I am working on a simple map app that will zoom into and follow my location using GeoCoordinateWatcher. The problem is, whenever I finish putting everything into place, InitializeComponents() always throws an exception and my C# code won`t recognize my XAML map controls. Is this some kind of bug or am I just a total fool?
CODE SAMPLE:
public MainPage()
{
this.InitializeComponent(); // This whole line is underlined red
this.NavigationCacheMode = NavigationCacheMode.Required;
}
public void CenterUserLocation()
{
// Center MyMap on user location
this.MyMap.Center = myPoint; //MyMap is underlined red
this.MyMap.ZoomLevel = 10; //MyMap is underlined red
}
UPDATE: (Class Definitions C#)
namespace MapApp{
public sealed partial class MainPage : Page
{
GeoCoordinateWatcher watcher;
private Geopoint myPoint;
UPDATE: (XAML)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MapApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
x:Class="MapApp.MainPage"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.BottomAppBar>
<CommandBar>
<!-- LOCATION BUTTON -->
<AppBarButton x:Uid="LocateAppBarButton"
x:Name="LocateAppBarButton"
Label="location"
Icon="Map"
Click="LocateAppBarButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Maps:MapControl x:Name="MyMap" HorizontalAlignment="Left" VerticalAlignment="Top" Height="580" Width="400"/>
</Grid>
</Page>
Your XAML and code-behind should be something like this:
XAML:
<Page x:Class="MyNamespace.MyCanvasCodeInline">
...
Code-behind:
namespace MyNamespace
{
public partial class MyCanvasCodeInLine // : Page - not needed but implied
...
Things you should check:
The code-behind class is defined as partial
The code-behind class qualified name (namespace + class name) is the same as the x:Class property of the XAML root node
The XAML root node is some class that implements IComponentConnector (Page, Window, UserControl, etc.)
Another way to do this is checking the xaml.cs file that gets generated "under" the XAML file, and compare its partial class definition with your code-behind class. They should match types, name and namespace.
Make sure that your XAML file has the correct Build Action (should be Page). I've seen Visual Studio change this when copy/pasting XAML files between projects.
If this isn't correct, you will get this error even though your class names and namespaces match between your XAML and code-behind.

WPF - canvas declaration xaml

Is it possible to declare a canvas in xaml without it being local to the MainWindow.xaml.cs page?
I have created a class called ImageBoard, but I need this class to have the xaml canvas held locally within it - and not within Mainwindow.xaml.cs.
What I want is the ImageBoard to act as the manager of this canvas.
The only other way I can imagine doing this is to have the ImageBoard derive from Canvas itself, so that it is the canvas. Then I could place this into the MainWindow.
At the moment I have this.
Xaml:
<Canvas HorizontalAlignment="Center" Height="370" VerticalAlignment="Center" Width="688"
x:Name="canvas1" Background="#f6f6f6"/>
But it can only be called from the MainWindow, as it is part of the MainWindow. Because of this I have to pass it to the ImageBoard to perform various Methods.
What I think would be best is this
Public class ImageBoard : Canvas
which would eliminate the need for passing the canvas.
I did try to declare a reference to the canvas, via the ImageBoard's constructor, but I think this would be bad practice; as I am, essentially, using the ImageBoard for all drawing purposes anyway. Why keep a canvas when I can turn the ImageBoard into a canvas, right?
You could build a ImageBoard UserControl:
XAML:
<UserControl x:Class="WpfMagic.ImageBoard"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Canvas x:Name="canvas" Background="Transparent" MouseLeftButtonUp="Canvas_MouseLeftButtonUp"></Canvas>
</Grid>
</UserControl>
Code behind:
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfMagic
{
public partial class ImageBoard : UserControl
{
public ImageBoard()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TextBlock tb = new TextBlock { Text = "*", FontSize = 20 };
tb.SetValue(Canvas.TopProperty, e.GetPosition(canvas).Y);
tb.SetValue(Canvas.LeftProperty, e.GetPosition(canvas).X);
canvas.Children.Add(tb);
}
}
}
And from you main page you can do:
<Window x:Class="WpfMagic.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMagic"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:ImageBoard></local:ImageBoard>
</Grid>
</Window>
This way you never directly manipulate the Canvas, it is abstracted away from the code using the ImageBoard, this is encapsulation.

C# User Control that can contain other Controls (when using it)

I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0

Categories

Resources