I have 2 classes (InnerModel and OuterModel). OuterModel contains 2 InnerModel instances. I would like to create UserControls for them (InnerUserControl and OuterUserControl). OuterUserControl contains 2 InnerUserControls. But I can't figure out how to make binding works in this case.
Below is the complete code of what I try to do.
Please advise how to fix it to get the same result as on a pic at the end.
Thanks in advance!
MainWindow.xaml.cs
<Window x:Class="NestedUserControl.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NestedUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="450">
<Grid>
<local:OuterUserControl x:Name="test"/>
</Grid>
MainWindow.xaml
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var model = new OuterModel("TEST1", "TEST2");
test.DataContext = model;
}
}
InnerModel.cs
public class InnerModel : INotifyPropertyChanged
{
public String Data
{
get { return data; }
set { data = value; }
}
private string data;
public event PropertyChangedEventHandler PropertyChanged;
public InnerModel(string _data) => data = _data;
public void OnPropertyChanged([CallerMemberName]string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
InnerUserControl.xaml
<UserControl x:Class="NestedUserControl.InnerUserControl"
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:NestedUserControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="450">
<Grid>
<TextBlock Text="{Binding Path=Data}"/>
</Grid>
InnerUserControl.xaml.cs
public partial class InnerUserControl : UserControl
{
public InnerUserControl()
{
InitializeComponent();
}
}
OuterModel.cs
public class OuterModel : INotifyPropertyChanged
{
public InnerModel model1;
public InnerModel model2;
public event PropertyChangedEventHandler PropertyChanged;
public OuterModel(string data1, string data2)
{
model1 = new InnerModel(data1);
model2 = new InnerModel(data2);
}
public void OnPropertyChanged([CallerMemberName]string prop = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
OuterUserControl.xaml
<UserControl x:Class="NestedUserControl.OuterUserControl"
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:NestedUserControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="450">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:InnerUserControl Grid.Row="0" x:Name="inner1" DataContext="model1"/>
<local:InnerUserControl Grid.Row="1" x:Name="inner2" DataContext="model2"/>
</Grid>
OuerUserControl.xaml.cs
public partial class OuterUserControl : UserControl
{
public OuterUserControl()
{
InitializeComponent();
}
}
Working binding MainWindow.xaml.cs
Welcome to SO!
Two problems here, first of all your inner models need to be properties, so change their declarations to this:
public InnerModel model1 {get; set;}
public InnerModel model2 {get; set;}
Second problem is with your bindings, you need to do this instead:
<local:InnerUserControl Grid.Row="0" x:Name="inner1" DataContext="{Binding model1}"/>
<local:InnerUserControl Grid.Row="1" x:Name="inner2" DataContext="{Binding model2}"/>
Related
I have 2 Pages with almost identical code behind (.xaml.cs file). They have different layout though (.xaml files are different). In Code-behind file, the only difference is the type of the variable. All other procedures/functions are exactly the same.
For example:
Page1:
public sealed partial class Page1 : Page
{
public List<CarVersion1> cars = new List<CarVersion1>();
public CarVersion1 currentCar;
...
private UpdatePrice(int p) {
currentCar.Price = p;
}
}
Page2:
public sealed partial class Page2 : Page
{
public List<CarVersion2> cars = new List<CarVersion2>();
public CarVersion2 currentCar;
...
private UpdatePrice(int p) {
currentCar.Price = p;
}
}
Is there anyway to use just 1 code-behind file instead of duplicating it?
I guess you need to create a ViewModel and put all your common logics there. Something like this:
CarVersions.cs
namespace Pages;
public class CarVersion
{
public string Version { get; set; } = string.Empty;
}
public class CarVersion1 : CarVersion
{
public CarVersion1()
{
Version = nameof(CarVersion1);
}
}
public class CarVersion2 : CarVersion
{
public CarVersion2()
{
Version = nameof(CarVersion2);
}
}
ViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
[ObservableObject]
public partial class ViewModel<T> where T : CarVersion, new()
{
[ObservableProperty]
private T carVersion = new();
}
Page1.xaml.cs
using Microsoft.UI.Xaml.Controls;
namespace Pages;
public sealed partial class Page1 : Page
{
public Page1()
{
this.InitializeComponent();
}
public ViewModel<CarVersion1> ViewModel { get; } = new();
}
Page2.xaml.cs
using Microsoft.UI.Xaml.Controls;
namespace Pages;
public sealed partial class Page2 : Page
{
public Page2()
{
this.InitializeComponent();
}
public ViewModel<CarVersion2> ViewModel { get; } = new();
}
Page1.xaml
<Page
x:Class="Pages.Page1"
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="using:Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<TextBlock Text="{x:Bind ViewModel.CarVersion, Mode=OneWay}" />
</Grid>
</Page>
Page2.xaml
<Page
x:Class="Pages.Page2"
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="using:Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<TextBlock Text="{x:Bind ViewModel.CarVersion, Mode=OneWay}" />
</Grid>
</Page>
MainWindow.xaml
<Window
x:Class="Pages.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="using:Pages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid ColumnDefinitions="*,*">
<local:Page1 Grid.Column="0" />
<local:Page2 Grid.Column="1" />
</Grid>
</Window>
You can create a BaseViewModel for both of the Pages. Then inherit it.
you can refer to this sample example for reference.
MySpecialView is a complex image control, I would like to reuse it from different views and pass its ViewModel as in this example.
MainWindow.xaml
<Window x:Class="YouBug.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:YouBug"
mc:Ignorable="d"
DataContext="{Binding MainViewModel}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:MySpecialView ViewModel="{Binding MySpecialViewModel}"></local:MySpecialView>
</Grid>
MainViewModel
public class MainViewModel
{
public MySpecialViewModel MySpecialViewModel { get; set; }
public MainViewModel()
{
MySpecialViewModel = new MySpecialViewModel();
//gets not displayed!
Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
}
}
MySpecialView.xaml
<UserControl x:Class="YouBug.MySpecialView"
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:YouBug"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Image Source="{Binding ImageSource}" />
</Grid>
MySpecialView.xaml.cs
public partial class MySpecialView : UserControl
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MySpecialViewModel), typeof(MySpecialView), new FrameworkPropertyMetadata(new MySpecialViewModel(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public MySpecialViewModel ViewModel { get { return (MySpecialViewModel)GetValue(ViewModelProperty); } set { SetValue(ViewModelProperty, value); } }
public MySpecialView()
{
DataContext = ViewModel;
InitializeComponent();
}
}
MySpecialViewModel
public class MySpecialViewModel : ViewModelBase
{
public BitmapSource imageSource { get; set; }
public BitmapSource ImageSource { get { return imageSource; }
set { if (value != imageSource)
{
imageSource = value; RaisePropertyChanged("ImageSource");
}
} }
public MySpecialViewModel()
{
//gets displayed
ImageSource = new BitmapImage(new Uri("C:\\Users\\user\\Pictures\\test.jpg"));
//gets displayed aswell
Task.Run(() => changeImage(10000, "C:\\Users\\user\\Pictures\\clickMe.png"));
}
public async void changeImage(int sleep, string uri)
{
await Task.Delay(sleep);
BitmapSource source = new BitmapImage(new Uri(uri));
source.Freeze();
ImageSource = source;
}
}
But whenever I assign MySpecialViewModels Properties from MainViewModel, the RaisePropertyChange event does not force the Image element or other bindings to update from the MySpecialViewModel.
What am I doing wrong here? Is this a general wrong approach?
You are too used to "View-First-Approach" (VFA). Your situation is better off using "ViewModel-First-Approach" (VMFA). In VFA, you place your child views from the main View, and each subview is linked to the respective ViewModel via DataContext.
In VMFA, your ViewModel holds references of sub-ViewModels. You expose these ViewModel references through property binding, and the View display them via DataTemplate.
MainWindow.xaml
<Window x:Class="YouBug.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:YouBug"
mc:Ignorable="d"
DataContext="{Binding MainViewModel}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type vm:MySpecialViewModel}">
<local:MySpecialViewModel />
</DataTemplate>
</Grid.Resources>
<ContentControl Content={Binding MySpecialView}" />
</Grid>
MainViewModel
public class MainViewModel : ViewModelBase // Not sure why you didn't subclass ViewModelBase in your question
{
private MySpecialViewModel _mySpecialViewModel;
public MySpecialViewModel MySpecialViewModel
{
get
{
return _mySpecialViewModel;
}
set
{
if (value != _mySpecialViewModel)
{
_mySpecialViewModel = value;
RaisePropertyChanged(); // The property changed method call
}
}
}
public MainViewModel()
{
MySpecialViewModel = new MySpecialViewModel();
//gets not displayed!
Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
}
}
MySpecialView does not need that DependencyProperty, nor set the DataContext. The DataContext is set automatically by the DataTemplate part. Your MySpecialViewModel can stay as it is now.
Edit
I just realized your MainWindow is not doing the DataContext correctly either.
MainWindow.xaml.cs
public partial class MainWindow: Window
{
public MainWindow()
{
InitializeComponents();
this.DataContext = new MainViewModel();
}
}
Do not specify viewmodel property in you view, use DataContext.
See the following code.
public partial class MySpecialView : UserControl
{
public MySpecialView()
{
InitializeComponent();
}
}
ViewModel for special:
public class MySpecialViewModel : ViewModelBase
{
public BitmapSource imageSource { get; set; }
public BitmapSource ImageSource { get { return imageSource; }
set { if (value != imageSource)
{
imageSource = value;
RaisePropertyChanged("ImageSource");
}
} }
public MySpecialViewModel()
{
//gets displayed
ImageSource = new BitmapImage(new Uri("C:\\Users\\user\\Pictures\\test.jpg"));
//gets displayed aswell
Task.Run(() => changeImage(10000, "C:\\Users\\user\\Pictures\\clickMe.png"));
}
public async void changeImage(int sleep, string uri)
{
await Task.Delay(sleep);
BitmapSource source = new BitmapImage(new Uri(uri));
source.Freeze();
ImageSource = source;
}
}
In XAML special:
<UserControl x:Class="YouBug.MySpecialView"
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:YouBug"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Image Source="{Binding ImageSource}" />
</Grid>
For main:
public class MainViewModel : ViewModelBase
{
public MySpecialViewModel SpecialViewModel
{
get { return _specialViewModel; }
set
{
if (value != _specialViewModel)
{
_specialViewModel= value;
RaisePropertyChanged("SpecialViewModel");
}
}
}
private MySpecialViewModel _specialViewModel;
public MainViewModel()
{
MySpecialViewModel = new MySpecialViewModel();
//gets not displayed!
Task.Run(() => MySpecialViewModel.changeImage(5000, "C:\\Users\\user\\Pictures\\Capture.PNG"));
}
}
And in XAML:
<Window x:Class="YouBug.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:YouBug"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<MainWindowViewModel/
</Window.DataContext>
<Grid>
<local:MySpecialView DataContext="{Binding Path=SpecialViewModel}"></local:MySpecialView>
</Grid>
I'm trying to realize a simple example of a UserControl, showing in a TextBox the current DateTime, updated four times each second.
I create a simple user control:
<UserControl x:Class="UC.TestUC"
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:UC"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="100">
<d:UserControl.DataContext>
<local:TestUC_VM/>
</d:UserControl.DataContext>
<Grid Background="Azure">
<TextBox Text="{Binding TestString}"/>
</Grid>
</UserControl>
Where its ViewModel is:
namespace UC
{
public class TestUC_VM : INotifyPropertyChanged
{
private string _testString;
public string TestString
{
get => _testString;
set
{
if (value == _testString) return;
_testString = value;
OnPropertyChanged();
}
}
public TestUC_VM()
{
TestString = "Test string.";
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow XAML:
<Window x:Class="UC.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UC"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="200">
<Window.DataContext>
<local:MainWindow_VM/>
</Window.DataContext>
<Window.Resources>
<local:TestUC_VM x:Key="TestUC_VM"/>
</Window.Resources>
<Grid>
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
</Grid>
</Window>
And its ViewModel:
namespace UC
{
public class MainWindow_VM
{
public TestUC_VM _uc_VM;
public MainWindow_VM()
{
_uc_VM = new TestUC_VM();
Task.Run(() => ChangeString());
}
public async Task ChangeString()
{
while (true)
{
_uc_VM.TestString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
await Task.Delay(250);
}
}
}
}
Even though I see with debugger that I'm passing through the TestString setter, the MainWindow is not updated.
I'm quite sure I'm missing something trivial in setting DataContext of UC in MainWindow, but I've not been able to find what after several hours of browsing and thinking.
Any help appreciated.
The expression
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
assigns the value of the TestUC_VM resource to the UserControl's DataContext. This is a different object than the _uc_VM member of the main view model, which you are later updating.
Turn the member into a public property
public TestUC_VM UcVm { get; } = new TestUC_VM();
and write
<local:TestUC DataContext="{Binding UcVm}"/>
Update the view model like this:
UcVm.TestString = ...
In a WPF project (code below) I have a UserControl of type MyUserControl with a dependency property, called MyOrientation of type Orientation.
On the MainWindow I have 2 instances of MyUserControl, where via XAML I set the Orientation property on one to be Horizontal and the other instance to be Vertical.
I have made the MyOrientation property a DP as I want the ability to set it directly in XAML as in this example or using a binding.
My problem is that when I run the project both instances of the UserControl show up with the Orientation = Horizontal?
Could someone please tell me what I am doing wrong and how to fix it?
Many thanks in advance.
Here is the code:
MYUSERCONTROLVIEWMODEL:
public class MyUserControlViewModel : ViewModelBase
{
private Orientation _myOrientation;
public Orientation MyOrientation
{
get { return _myOrientation; }
set
{
if (_myOrientation == value)
return;
_myOrientation = value;
OnPropertyChanged();
}
}
}
MYUSERCONTROL.XAML
<UserControl x:Class="TestUserControlDPProblem.MyUserControl"
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:TestUserControlDPProblem"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="root">
<Grid.DataContext>
<local:MyUserControlViewModel/>
</Grid.DataContext>
<StackPanel Orientation="{Binding MyOrientation}">
<TextBlock>Hello</TextBlock>
<TextBlock>There</TextBlock>
</StackPanel>
</Grid>
MYUSERCONTROL CODE BEHIND:
public partial class MyUserControl : UserControl
{
MyUserControlViewModel _vm;
public MyUserControl()
{
InitializeComponent();
_vm = root.DataContext as MyUserControlViewModel;
}
public static readonly DependencyProperty MyOrientationProperty = DependencyProperty.Register("MyOrientation", typeof(Orientation), typeof(MyUserControl), new FrameworkPropertyMetadata(Orientation.Vertical, new PropertyChangedCallback(OnMyOrientationChanged)));
private static void OnMyOrientationChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var myUserControl = o as MyUserControl;
myUserControl?.OnMyOrientationChanged((Orientation)e.OldValue, (Orientation)e.NewValue);
}
protected virtual void OnMyOrientationChanged(Orientation oldValue, Orientation newValue)
{
_vm.MyOrientation = newValue;
}
public Orientation MyOrientation
{
get
{
return (Orientation)GetValue(MyOrientationProperty);
}
set
{
SetValue(MyOrientationProperty, value);
}
}
}
MAINWINDOW.XAML
<Window x:Class="TestUserControlDPProblem.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestUserControlDPProblem"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<local:MyUserControl Margin="10" MyOrientation="Horizontal"/>
<local:MyUserControl Margin="10" MyOrientation="Vertical"/>
</StackPanel>
</Grid>
The "internal" view model of the UserControl makes no sense and should not be there. You should instead bind directly to the dependency property by means of a RelativeSource or ElementName Binding:
<StackPanel Orientation="{Binding MyOrientation,
RelativeSource={RelativeSource AncestorType=UserControl}}">
You wouldn't even need the PropertyChangedCallback:
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty MyOrientationProperty =
DependencyProperty.Register(
nameof(MyOrientation), typeof(Orientation), typeof(MyUserControl),
new FrameworkPropertyMetadata(Orientation.Vertical));
public Orientation MyOrientation
{
get { return (Orientation)GetValue(MyOrientationProperty); }
set { SetValue(MyOrientationProperty, value); }
}
}
Using ArcGIS Runtime .Net SDK 10.2.7, with MVVM pattern I am getting 'System.NullReferenceException'. in ViewModel constructor at:
this.mapView.Map.Layers.Add(localTiledLayer);
What am I doing wrong?
What I have is:
1- ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private Model myModel = null;
private MapView mapView = null;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
var URI = "D:/GIS/Runtime/Tutorial/VanSchools.tpk";
ArcGISLocalTiledLayer localTiledLayer = new ArcGISLocalTiledLayer(URI);
localTiledLayer.ID = "SF Basemap";
localTiledLayer.InitializeAsync();
this.mapView.Map.Layers.Add(localTiledLayer);
}
public string TilePackage
{
get { return this.myModel.TilePackage; }
set
{
this.myModel.TilePackage = value;
}
}
2- Model.cs
public class Model
{
private string tilePackage = "";
public Model() { }
public string TilePackage
{
get { return this.tilePackage; }
set
{
if (value != this.tilePackage)
{
this.tilePackage = value;
}
}
}
3- MainWindow.xaml
<Window x:Class="SimpleMVVM.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
xmlns:local="clr-namespace:SimpleMVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:ViewModel x:Key="VM"/>
</Window.Resources>
<Grid DataContext="{StaticResource VM}">
<Grid.RowDefinitions>
<RowDefinition Height="400" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<esri:MapView x:Name="MyMapView" Grid.Row="0"
LayerLoaded="MyMapView_LayerLoaded" >
<esri:Map> </esri:Map>
</esri:MapView>
</Grid>
</Window>
4- MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MyMapView_LayerLoaded(object sender, LayerLoadedEventArgs e)
{
if (e.LoadError == null)
return;
Debug.WriteLine(string.Format("Error while loading layer : {0} - {1}", e.Layer.ID, e.LoadError.Message));
}
}
It looks like the problem is that mapView has not been set to an instance of an object. Do you need to say something like:
this.mapView = new Mapview();
this.mapView.Map.Layers.Add(localTiledLayer);
It seems that you aren't hooking up the binding but in general you shouldn't have reference to the MapView from your ViewModel and should bind MapView.Map instead. Map is considered being a model that defines how your MapView looks.
In your view make sure that you have the binding setup
<Window.Resources>
<vm:MainViewModel x:Key="vm"></vm:MainViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
<esri:MapView x:Name="MyMapView"
Map="{Binding Map}"
LayerLoaded="MyMapView_LayerLoaded">
</esri:MapView>
</Grid>
Then in your ViewModel you can do something like this
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
// Create map that is concidered to being a model that defines the content
// of the map
var map = new Map();
// Add your layers to the map
var testServicePath = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
map.Layers.Add(new ArcGISTiledMapServiceLayer(new Uri(testServicePath)));
// map.Layers.Add(new ArcGISLocalTiledLayer("add your path here"));
Map = map;
}
private Map _map;
public Map Map
{
get
{
return _map;
}
set
{
_map = value;
NotifyPropertyChanged(nameof(Map));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}