Bind XAML Map control to Code behind - c#

EXPLANATION:
I can't use code behind because I'm using some interfaces who hare injected with Ninject, I can't just do service = new service() because there are a lot more dependencies behind that one. As for the question about the type name as a converter parameter, I found this solution best for binding my model to my UI. I use this for all of my modules and is working great. Just my Map control is not working at all.
Atm I'm trying to include a Microsoft.Phone.Maps.Controls.Map control in my Windows Phone 8 app. But I can't seem to get it working.
Because of Dependency injection and stuff I can't use the code behind of my xaml page. So I made myself a MapController who is linked to the Xaml as a DataContext. But I got some problems with it.
XAML:
<phone:PhoneApplicationPage
...
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
DataContext="{Binding Source={StaticResource DynamicLocator}, Converter={StaticResource IndexConverter}, ConverterParameter='BaseCpr.Plugins.ShopLocator.Model.MapController,BaseCpr.Plugins'}">
<Grid>
<maps:Map DataContext="{Binding MapControl, Mode=TwoWay}">
</maps:Map>
</Grid>
</phone:PhoneApplicationPage>
MapController:
public MapController(IService Service) {
//Service is used here a lot
MapControl = new Map() {
CartographicMode = MapCartographicMode.Road,
Heading = 0,
Pitch = 0,
ZoomLevel = 7,
Center = new GeoCoordinate(50.50, 4.000)
};
}
private Map mapControl;
public Map MapControl {
get { return mapControl; }
set {
if (mapControl != value) {
mapControl = value;
RaisePropertyChanged(() => MapControl);
}
}
}
The link between the XAML and my MapController is working (tested it with simple boolean bindings). And when I'm debugging the getter of MapControl get called. But my map on my screen is still at the default view. Can someone help me out what I'm doing wrong?
Thanks
EDIT 2: (some more info)
In my MapController I have the following function as well:
public async void getPhoneLocation() {
try
{
//getting location and put it in geoposition
var cord = new GeoCoordinate(geoposition.Coordinate.Latitude, geoposition.Coordinate.Longitude);
MapControl.SetView(cord, 16, 0, 0, MapAnimationKind.Parabolic);
CreateAndAddUserMarker(cord);
}
catch (Exception ex)
{
//TODO
}
}
This is why I can't just bind all Map properties of my XAML map control. I need to call the SetView() function as well. I also need to add Layers to my map to add pins on the map. getPhoneLocation() is called in the MapController constructor.

To solve my problem by making a MapBehavior and bind a ViewModel property to a Dependency Property from the MapBehavior.
That way, when my Location get's updated it will fire the Dependency Property PropertyChangedCallback.
public DependencyProperty LocationProperty = DependencyProperty.Register(
"Location", typeof(GeoCoordinate), typeof(MapBehavior), new PropertyMetadata(null, (sender, args) =>
{
if (args.NewValue != null && args.NewValue != args.OldValue)
{
var sendMap = ((MapBehavior)sender);
sendMap.AssociatedObject.SetView((GeoCoordinate)args.NewValue, 14, 0, 0, MapAnimationKind.Parabolic);
}
}));
In sender is the Map control, that way the SetView() method on your MapControl can be used :)
It took me a while to get this solution so I wanted to share it anyway

Related

How to change SystemAccentColor at runtime (UWP)?

The app has a GridView in which each item is a color that the user can choose to customize the UI overriding the default SystemAccentColor (the one is defined by user on their system).
I managed to get the color of the item but even though I assign it as new value for SystemAccentColor I am not able to update the UI.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
// FIRST APROACH -----
GridViewItem gridViewItem = GVColors.ContainerFromItem(e.ClickedItem) as GridViewItem;
Ellipse ellipseItem = gridViewItem.FindDescendant<Ellipse>();
var theColor = (SolidColorBrush)ellipseItem.Fill;
Application.Current.Resources["SystemAccentColor"] = theColor;
// SECOND APPROACH ----
Windows.UI.Color theColor2 = new Windows.UI.Color
{
A = 1,
R = 176,
G = 37,
B = 37
};
var root = (FrameworkElement)Window.Current.Content;
root.Resources["SystemAccentColor"] = theColor2;
}
I'm currently reading this blog entry XAML Brewer, by Diederik Krols: Using a Dynamic System Accent Color in UWP but I want to know if the community knows another approach to change the accent color at runtime (or a method that I'm not aware of to Update/refresh the UI).
I assign it as new value for SystemAccentColor I am not able to update the UI.
Since you statically bind SystemAccentColor and it doesn't implement INotifyPropertyChanged interface, event though the value of SystemAccentColor changes, the UI which bound with it won't update directly.
Based on your requirement, you can add a class which implements INotifyPropertyChanged interface and add the SystemAccentColor as property in it. Then init the class instance in Application.Resources. After that, bind the UI with the SystemAccentColor property. For example, I create a class named SystemAccentColorSetting.
SystemAccentColorSetting.cs:
public class SystemAccentColorSetting : INotifyPropertyChanged
{
private SolidColorBrush systemAccentColor = new SolidColorBrush(Colors.Red);
public SolidColorBrush SystemAccentColor
{
get {
return systemAccentColor;
}
set {
systemAccentColor = value; OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
App.xaml:
<Application.Resources>
<ResourceDictionary>
<local:SystemAccentColorSetting x:Key="SystemAccentColorSetting"/>
</ResourceDictionary>
</Application.Resources>
Usage:
Assume that we bind the Background of Button with the SystemAccentColor property.
.xaml:
<Button x:Name="MyButton" Background="{Binding SystemAccentColor, Source={StaticResource SystemAccentColorSetting}}">hello</Button>
.cs:
If you want to change the value of Background, just change the SystemAccentColor property.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
GridViewItem gridViewItem = GVColors.ContainerFromItem(e.ClickedItem) as GridViewItem;
Ellipse ellipseItem = gridViewItem.FindDescendant<Ellipse>();
var theColor = (SolidColorBrush)ellipseItem.Fill;
((SystemAccentColorSetting)Application.Current.Resources["SystemAccentColorSetting"]).SystemAccentColor = theColor;
}
Starting from Win 10 1809 (build 17763), you can use the ColorPaletteResources Class.
By using it, you can change not only the Accent color at runtime, but also all the other system default colors for UI controls.
Due to a bug, you forst have to declare that ColorPaletteResources object into your App's XAML resources (with a key) and then you can use it at runtime.
Another bug is that, from what I have experimented some time ago, you can only change the Accent color for now, so you are lucky.
To see it in action, you can download Fluent XAML Theme Editor from the Windows Store or from GitHub.
Here is the link to the class itself and to some guidelines on how to use it.

Dependency property Canvas not binding

I'm making a tool where I can edit images, so I have a custom Control to draw on a canvas. For this control I bound the resolution and the color of the brush which work perfectly fine, but when I try to bind a Canvas I always get null in myViewModel.
I tried binding a List instead, but that didn't work either. I've been trying to figure out what the problem is for about 12 hours now so I think it's time to ask you guys for help so I can maybe figure out what's wrong.
I used to work with singletons for my viewmodels, and then I could link to the Canvas just fine, but then I realised this is a bad way of working so I tried to bind it with dependency properties.
Whenever I try to access the Canvas it has a value of null, even though I initialized it.
Control
public static readonly DependencyProperty CanvasProperty = DependencyProperty.Register(
"CanvasToDraw", typeof(Canvas), typeof(DrawCanvas), new PropertyMetadata(default(Canvas), null, null));
public Canvas CanvasToDraw
{
get { return (Canvas)GetValue(CanvasProperty); }
set { SetValue(CanvasProperty, value); }
}
public DrawCanvas()
{
InitializeComponent();
CanvasToDraw = CanvasGrid;
}
XAML
<Controls:DrawCanvas x:Name="DrawCanvas1" Resolution="{Binding Resolution}" CanvasToDraw="{Binding DrawingCanvas}" RectangleList="{Binding RectangleList, ElementName=DrawCanvas1}" ColorToDraw="{Binding SelectedColor}" HorizontalAlignment="Left" Height="256" Margin="10,40,0,0" VerticalAlignment="Top" Width="256" />
VIEWMODEL
private Canvas _drawingCanvas;
public Canvas DrawingCanvas
{
get { return _drawingCanvas; }
set
{
_drawingCanvas = value;
OnPropertyChanged("DrawingCanvas");
}
}
I can provide more code if needed, but this is most of the relevant code. And yes I do use INotifyPropertyChanged, since I have other things that I bound that do work.
I've been looking for a solution for so long, but the answer was so simple. I assumed the binding was two way, but appareantly I had to set Mode=TwoWay and everything worked like a charm. (Linking an observeablecollection, not a Canvas)

WPF UserControl command binding not udpateing UI MVVM

I have window "ClientsWindow" and it's view model class "ClientsViewModel". In ViewModel i defined property "Clients" and bound it to DataGrid's itemssource property:
private ObservableCollection<tblClient> clients;
public ObservableCollection<tblClient> Clients
{
get { return clients; }
set
{
clients = value;
OnPropertyChanged("Clients");
}
}
In my window's constructor I set this property to new value by calling the method from wcf service like this:
Clients = new ObservableCollection<tblClient>(wcf.FilterClients(PageIndex, PageSize));
And it works perfect, I get 10 records from wcf service as it should be and the list is shown in datagrid. I insert some usercontrol which I want to use for datagrid pagination. It has ChangedIndexCommand defined like this:
ChangedIndexCommandProperty =
DependencyProperty.Register("ChangedIndexCommand", typeof(ICommand), typeof(GridPaging), new UIPropertyMetadata(null));
public ICommand ChangedIndexCommand
{
get { return (ICommand)GetValue(ChangedIndexCommandProperty); }
set { SetValue(ChangedIndexCommandProperty, value); }
}
I tried to bind command form my window's viewmodel to this command, so i did it this way:
private ICommand _cmdChangedIndex;
public ICommand cmdChangedIndex
{
get
{
if (_cmdChangedIndex == null)
{
_cmdChangedIndex = new DelegateCommand(delegate()
{
worker.DoWork += worker_FilterClientsList;
worker.RunWorkerCompleted += worker_FilterClientListCompleted;
worker.RunWorkerAsync();
});
}
return _cmdChangedIndex;
}
}
private void worker_FilterClientsList(object sender, DoWorkEventArgs e)
{
try
{
ServiceClient wcf = new ServiceClient();
Clients = new ObservableCollection<tblClient>(wcf.FilterClients(PageIndex, PageSize));
TotalCount = wcf.ReturnClientsCount();
}
catch (Exception ex)
{
}
}
private void worker_FilterClientListCompleted(object sender, RunWorkerCompletedEventArgs e)
{
worker.DoWork -= worker_FilterClientsList;
}
And here is the xaml:
<pc:GridPaging PageIndex="{Binding PageIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PageSize="{Binding PageSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TotalCount="{Binding TotalCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" x:Name="clientsPagingControl"
ChangedIndexCommand="{Binding cmdChangedIndex, UpdateSourceTrigger=PropertyChanged}"
Visibility="Visible" VerticalAlignment="Top"
/>
So, while debugging everything works perfect! My command is fired when i click on the button of my userconrol, the method from wcf service is called properly and it returns new collection of items(count 2, as expected), my "Clients" property is set to new value BUT, UI still showing 10 items in my datagrid. I just cant figure out what is wrong?! Is this wrong way of binding commands to custom user controls?? Also let me note that, PageIndex, PageSize and TotalCount properties are of type int, and i bound them to my viewmodel properties, and they work perfect. But what is the problem with my command? I tried to be as clear as I could hope that you will understand what my problem is, and for any more info, please leave the comment.
OnPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
DataGrid binding:
<DataGrid IsReadOnly="True" Name="dgClients" AutoGenerateColumns="False" ItemsSource="{Binding Path=Clients, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns>
....
</DataGrid.Columns>
</DataGrid>
Just a thought, but it looks like you are using a BackgroundWorker class in your ICommand? In the worker_FilterClientsList method, you are setting the "Clients" observable collection property. I don't think you are able to manipulate the UI from within DoWork (it's running on a different thread). Try removing the try..catch block to see if it's hiding such an error.
You normally have to update the UI from the RunWorkerCompleted delegate (your worker_FilterClientListCompleted method).
Ok, so judging by your question, answers and the many comments, it would seem that your problem is un-reproducible. This means that you are on your own, as far as fixing your problem goes. However, this is not as bad as it sounds.
As there is no obvious problem with your displayed code, I cannot point out where your error lies. However, I can put you onto the right path to fix your own problem. It will take some time and effort on your part, but 'no pain... no gain', as they say.
One of the best ways that you find the problem in a complex project is to simplify it in a new, empty project. Normally when doing this, one of two things happens: either you find out what the problem was, or you create a concise working example that demonstrates your problem, which you can then post here (maybe as a new question, or instead of your current code). It's usually a win-win situation.
As it happens, the StackOverflow Help Center has a page to help with this. Please follow the advice in the How to create a Minimal, Complete, Tested and Readable example page to help you to simplify your problem.
One final point that I'd like to make is that normally in an application, the data access layer is separate from the UI. If you separate your different concerns like this, you will also find that it simplifies the situation further.

How to bind Windows Form Control to a "Grid" Panel using MVVM in WPF

I am attempting to use MVVM to Bind a Windows Form Control to a panel in WPF. My overall objective is to be able to dynamically change which specific Windows Form Control I will use as I plan on having potentially several available.
Right now, I have been able to get this to work by having the application launch a callback on initialization which accesses the grid object by name. Here is how XAML currently looks:
<Grid Name="WindowsControlObject"></Grid>
The Callback looks like the following:
private void WindowLoaded(object sender, RoutedEventArgs e)
{
System.Windows.Forms.Integration.WindowsFormsHost host =
new System.Windows.Forms.Integration.WindowsFormsHost();
System.Windows.Forms.Control activeXControl = new SomeWindowsControl();
host.Child = activeXControl;
this.WindowsControlObject.Children.Add(host);
}
While this works, I am trying to fully utilize the MVVM pattern, as such is there a way I can do something like the following in the XAML/ModelView:
XAML:
<Grid Content="{Binding WindowsControl"></Grid>
In my ModelView:
public class MyModelView
{
public Grid WindowsControl;
public MyModelView{
WindowsControl = new Grid;
System.Windows.Forms.Integration.WindowsFormsHost host =
new System.Windows.Forms.Integration.WindowsFormsHost();
System.Windows.Forms.Control activeXControl = new SomeWindowsControl();
host.Child = activeXControl;
WindowsControl.WindowsControlObject.Children.Add(host);
}
}
Am I even right in my exploration/possible approach? It has occurred to me that I might need to use some other type of panel (other than grid), but haven't found anything obvious yet. If it can't be done, I have a solution, just not a very clean one.
Doing more digging, it turns out that I really wanted to bind this to a "ContentControl" tag, as follows:
XAML:
<ContentControl Content="{Binding WindowsControl}"/>
ViewModel:
private System.Windows.Forms.Control _myControl;
public WindowsFormsHost STKObject
{
get
{
return new WindowsFormsHost() { Child = _myControl};
}
}

Dynamically Generate simple Views from ViewModels in WPF?

I'm slowly learning WPF using this article and other resources.
I am focusing on the application logic - defining the model + viewModel, and creating commands that operate on these. I have not yet looked at the view and the .xaml format.
While I am working on the logic, I want to have a view that can render any viewModel I bind to it. The view should
Render any public string properties as text boxes, and bind the text box to the property
Render the name of the property as a label.
Render any public 'Command' property as a button, and bind the button to the command (perhaps only if the command takes no arguments?)
Is something like this possible while maintaing the MVVM design pattern? If so, how would I achieve it? Also, the article suggests to avoid using .xaml codebehind - can this view be implemented in pure xaml?
I don't think it is possible in XAML only. If you want to generate your views in runtime then you have to just use reflection over your ViewModels and generate controls accordingly. If you want to generate views at compile time then you can generate xaml files from your ViewModels at build time with some template engine (like T4 or string template) or CodeDom. Or you can go further and have some metadata format (or even DSL) from which you will generate both models and views and so on. It is up to your app needs.
And also in MVVM code-behind is Ok for visual logic and binding to model/viewmodel that can't be done in XAML only.
I'm not sure this is an appropriate use for a "pure MVVM" approach, certainly not everything is going to be achieved simply by binding. And I'd just throw away the idea of avoiding using code-behind for your "view" here, this is an inherently programmatic task. The one thing you should stick to is giving the ViewModel no knowledge of the view, so that when you replace it with the "real thing" there is no work to do.
But certainly seems reasonable thing to do; it almost sounds more like a debugging visualiser - you may be able to leverage an existing tool for this.
(If you did want to do this in mostly XAML with standard ItemsControls and templates you might write a converter to expose properties of your ViewModel by reflection in some form that you can bind to, a collection of wrapper objects with exposed metadata, but I think ensuring that the properties exposed are properly bindable would be more work than it's worth)
I'm halfway through implementing this now, I hope the following code will help anyone else trying to do this. It might be fun to turn into a more robust library.
AbstractView.xaml:
<UserControl x:Class="MyApplication.View.AbstractView"
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">
<StackPanel Name="container">
</StackPanel>
</UserControl>
AbstractView.xaml.cs:
public partial class AbstractView : UserControl
{
public AbstractView()
{
InitializeComponent();
DataContextChanged += Changed;
}
void Changed(object sender, DependencyPropertyChangedEventArgs e)
{
object ob = e.NewValue;
var props = ob.GetType().GetProperties();
List<UIElement> uies = new List<UIElement>();
foreach (var prop in props)
{
if (prop.PropertyType == typeof(String))
uies.Add(makeStringProperty(prop));
else if (prop.PropertyType == typeof(int))
uies.Add(makeIntProperty(prop));
else if (prop.PropertyType == typeof(bool))
uies.Add(makeBoolProperty(prop));
else if (prop.PropertyType == typeof(ICommand))
uies.Add(makeCommandProperty(prop));
else
{
}
}
StackPanel st = new StackPanel();
st.Orientation = Orientation.Horizontal;
st.HorizontalAlignment = HorizontalAlignment.Center;
st.Margin = new Thickness(0, 20, 0, 0);
foreach (var uie in uies) {
if (uie is Button)
st.Children.Add(uie);
else
container.Children.Add(uie);
}
if (st.Children.Count > 0)
container.Children.Add(st);
}
UIElement makeCommandProperty(PropertyInfo prop)
{
var btn = new Button();
btn.Content = prop.Name;
var bn = new Binding(prop.Name);
btn.SetBinding(Button.CommandProperty, bn);
return btn;
}
UIElement makeBoolProperty(PropertyInfo prop)
{
CheckBox bx = new CheckBox();
bx.SetBinding(CheckBox.IsCheckedProperty, getBinding(prop));
if (!prop.CanWrite)
bx.IsEnabled = false;
return makeUniformGrid(bx, prop);
}
UIElement makeStringProperty(PropertyInfo prop)
{
TextBox bx = new TextBox();
bx.SetBinding(TextBox.TextProperty, getBinding(prop));
if (!prop.CanWrite)
bx.IsEnabled = false;
return makeUniformGrid(bx, prop);
}
UIElement makeIntProperty(PropertyInfo prop)
{
TextBlock bl = new TextBlock();
bl.SetBinding(TextBlock.TextProperty, getBinding(prop));
return makeUniformGrid(bl, prop);
}
UIElement makeUniformGrid(UIElement ctrl, PropertyInfo prop)
{
Label lb = new Label();
lb.Content = prop.Name;
UniformGrid u = new UniformGrid();
u.Rows = 1;
u.Columns = 2;
u.Children.Add(lb);
u.Children.Add(ctrl);
return u;
}
Binding getBinding(PropertyInfo prop)
{
var bn = new Binding(prop.Name);
if (prop.CanRead && prop.CanWrite)
bn.Mode = BindingMode.TwoWay;
else if (prop.CanRead)
bn.Mode = BindingMode.OneWay;
else if (prop.CanWrite)
bn.Mode = BindingMode.OneWayToSource;
return bn;
}
}
Pointer: Generate a dynamic DataTemplate as a string tied to the specific VM (Target). Parse it via XamlReader. Plonk it into your app resources in code.
Just an idea.. run with it.. Should be done by some type other than the View or the ViewModel.

Categories