Hi I would like to know what the easies way to rotate a Grid would be.
I have 4 pages:
private static Figure[] array;
public App ()
{
Initialize(array); // Fills array with figures with ImageSources
InitializeComponent ();
MainPage = new Page(array,Color.Red);
}
class Figure
{
private ImageSource Source {get; set;}
public Figure(ImageSource source)
{
Source = source;
}
}
class Page
{
private Color Color;
private Grid Grid;
public Page (Figure[] Figures, Color color)
{
Color = color;
// Now this is where I need help...
}
}
I would like to have a Grid always the same size and always filled with the same array but depending on the Color the orientation should change. In fact the whole Grid should just rotate 90degrees depending on the Color. These Grids should have ImageButtons which bind to the Imagesource of the figure (with a Converter). I thought about Creating 4Grids in Xaml and implement everything by hand and just give every page the custom Grid. Another option I came up with was creating one Grid only and using the rotation-method of the Grid (but with this option I have to rotate back every child of the Grid as otherwise the pictures would rotate with the Grid... As I think both solutions are quite inconvenient I was wondering what other options I have. Maybe someone can help me? Thanks a lot...
Example of ImageSources that change based on a setting.
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestBugs.MainPage">
<StackLayout>
<Label Text="Test"/>
<Grid ColumnDefinitions="50,50" RowDefinitions="50,50">
<Image Grid.Row="0" Grid.Column="0" Source="{Binding Source1A}" BackgroundColor="Red"/>
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Source1B}" BackgroundColor="Green"/>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding Source2A}" BackgroundColor="Blue"/>
<Image Grid.Row="1" Grid.Column="1" Source="{Binding Source2B}" BackgroundColor="Yellow"/>
</Grid>
</StackLayout>
</ContentPage>
C#:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TestBugs
{
public partial class MainPage : ContentPage
{
// REPLACE "TestBugs" with your project's assembly name.
public const string AssemblyName = "TestBugs";
public enum Orientation
{
One, Two, Three, Four
}
const int NOrientations = 4;
public MainPage()
{
// Assuming stored locally in files or resources.
// If need server queries, recommend not doing this in constructor.
LoadOurImages();
InitializeComponent();
// In this simple example, the binding sources are in the page itself.
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BackgroundTestLoop();
}
static Random Rand = new Random();
private void BackgroundTestLoop()
{
Task.Run(async () =>
{
const int NTimes = 20;
for (int i = 0; i < NTimes; i++)
{
await Task.Delay(3000);
Orientation nextOrient = (Orientation)Rand.Next(NOrientations);
// Only affect UI from main thread.
Device.BeginInvokeOnMainThread(() =>
{
Orient = nextOrient;
});
}
});
}
public Orientation Orient {
get => _orient;
set
{
_orient = value;
// When Orient changes, that affects the values of these properties.
// OnPropertyChanged is from super-class BindableObject.
OnPropertyChanged(nameof(Source1A));
OnPropertyChanged(nameof(Source1B));
OnPropertyChanged(nameof(Source2A));
OnPropertyChanged(nameof(Source2B));
}
}
private Orientation _orient = Orientation.One;
// Public getters. These change when Orient changes.
public ImageSource Source1A => Sources[Indexes1A[(int)Orient]];
public ImageSource Source1B => Sources[Indexes1B[(int)Orient]];
public ImageSource Source2A => Sources[Indexes2A[(int)Orient]];
public ImageSource Source2B => Sources[Indexes2B[(int)Orient]];
List<string> ResourcePaths = new List<string> {
"apple.png", "banana.png", "car.png", "dog.png"};
List<ImageSource> Sources = new List<ImageSource>();
// Change these as needed.
List<int> Indexes1A = new List<int> { 0, 1, 2, 3 };
List<int> Indexes1B = new List<int> { 1, 2, 3, 0 };
List<int> Indexes2A = new List<int> { 2, 3, 0, 1 };
List<int> Indexes2B = new List<int> { 3, 0, 1, 2 };
private void LoadOurImages()
{
foreach (var path in ResourcePaths)
Sources.Add(CreateOurSource(path));
}
private ImageSource CreateOurSource(string resourcePath)
{
// For embedded resources stored in project folder "Media".
var resourceID = $"{AssemblyName}.Media.{resourcePath}";
// Our media is in the cross-platform assembly. Find that from this page.
Assembly assembly = this.GetType().GetTypeInfo().Assembly;
ImageSource source = ImageSource.FromResource(resourceID, assembly);
return source;
}
}
}
Related
I am currently working with a coverflowview by using this nuget:
https://github.com/AndreiMisiukevich/CardView
Works very well with the binding when I use a regular image or cachedimage (ffimageloading nuget). However now i try to grayscale the image by using a custom control. I successfully run the code to grayscale it (when propertyischanged to IsSelectable true), but for some reason the image is not showing at all, if i remove the grayscale logic, the image shows nicely.
<cards:CoverFlowView PositionShiftValue="40"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HeightRequest="360">
<cards:CoverFlowView.ItemTemplate>
<DataTemplate>
<AbsoluteLayout HeightRequest="360">
<controls:GrayScaleImage Aspect="AspectFill"
AbsoluteLayout.LayoutBounds="0.0, 0.5, 1, 1"
AbsoluteLayout.LayoutFlags="All"
Source="{Binding ProgramDeserialized.Image}"
IsSelectable="{Binding IsSelectable}"/>
</AbsoluteLayout>
</DataTemplate>
</cards:CoverFlowView.ItemTemplate>
</cards:CoverFlowView>
And custom control:
public class GrayScaleImage : CachedImage
{
public static BindableProperty IsSelectableProperty = BindableProperty.Create(nameof(IsSelectable), typeof(bool), typeof(GrayScaleImage), true, propertyChanged: UpdateImage);
public bool IsSelectable
{
get { return (bool)GetValue(IsSelectableProperty); }
set { SetValue(IsSelectableProperty, value); }
}
private static void UpdateImage (BindableObject bindable, object oldColor, object newColor)
{
var view = (GrayScaleImage)bindable;
if (!view.IsSelectable)
{
var transformations = new System.Collections.Generic.List<ITransformation>() {
new GrayscaleTransformation()
};
view.Transformations = transformations;
}
}
}
Not sure what the issue might be. When i did it on a regular stacklayout bindable list, and applied the same logic, it works, so my gutfeeling is that there is some issue with the coverflowview nuget.
How did you set binding for the Image? I created a sample to test the function code, the grayScale image works fine.
Check the screenshot:
https://us.v-cdn.net/5019960/uploads/editor/ab/jqki5zvo7cfw.gif
Here is the code about the model class and the viewModel class, you could refer to it.
public class CustomModel
{
public Xamarin.Forms.ImageSource MyImage { get; set; }
public bool IsSelectable { get; set; }
}
public class CustomViewModel
{
public ObservableCollection<CustomModel> Items { get; set; }
public CustomViewModel()
{
Items = new ObservableCollection<CustomModel>();
//add the data
}
}
I want to display images saved in SQL server management studio in FlipView using Universal Windows Platform. The data type for the Image is nvarchar using Image Path.
I've put in image url in the last record to test out if my code actually goes through the database.
And it does!
It just displays the last record which is an image url:
So i don't know why it cannot display the images that's saved in the database using image path. Pls help! Also, apologies if my coding doesn't make sense. I'm very new to programming :/
MainPage.html:
<FlipView x:Name="TheFlipView" SelectionChanged="DisplayedItemChanged" HorizontalAlignment="Stretch" Margin="-117,66,0,0"
Grid.Row="1"
Grid.ColumnSpan="3"
VerticalAlignment="Stretch" Height="1000" Width="1392">
<FlipView.ItemTemplate>
<DataTemplate >
<Grid Margin="0,0,0,10">
<Image HorizontalAlignment="Center" VerticalAlignment="Top" Source="{Binding ImgPath}"
Stretch="Fill" Margin="123,0,0,0" Width="1269" Height="899" />
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
MainPage.xaml.cs:
namespace TMD_Display.Views
{
public sealed partial class MainPage : Page
{
private string connectionString = #"Server=LAPTOP-IQQCR1C1\SQLEXPRESS;Database=SampleDB;User Id=alish;Password=polylife16;";
//Make a place to store the timer
private readonly DispatcherTimer _timer;
//Make a place to store the last time the displayed item was set
private DateTime _lastChange;
public int Id { get; set; }
public string ImgPath { get; set; }
public MainPage()
{
InitializeComponent();
//Configure the timer
_timer = new DispatcherTimer
{
//Set the interval between ticks (in this case 2 seconds to see it working)
Interval = TimeSpan.FromSeconds(2)
};
//Change what's displayed when the timer ticks
_timer.Tick += ChangeImage;
//Start the timer
_timer.Start();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var images = await GetImagesAsync();
base.OnNavigatedTo(e);
}
public async Task<IEnumerable<Models.Images>> GetImagesAsync()
{
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var images = await conn.QueryAsync<Models.Images>(
#"SELECT ImgId As Id,ImgPath
FROM ImageSave");
TheFlipView.ItemsSource = images;
//This is the code to be changed
return images;
}
}
private void ChangeImage(object sender, object o)
{
//Get the number of items in the flip view
//var totalItems = TheFlipView.Items.Count;
var totalItems = TheFlipView.Items.Count;
//Figure out the new item's index (the current index plus one, if the next item would be out of range, go back to zero)
var newItemIndex = (TheFlipView.SelectedIndex + 1) % totalItems;
//Set the displayed item's index on the flip view
TheFlipView.SelectedIndex = newItemIndex;
}
private void DisplayedItemChanged(object sender, SelectionChangedEventArgs e)
{
//Show the time deltas...
var currentTime = DateTime.Now;
if (_lastChange != default(DateTime))
{
TimeDelta.Text = (currentTime - _lastChange).ToString();
}
_lastChange = currentTime;
//Since the page is configured before the timer is, check to make sure that we've actually got a timer
if (!ReferenceEquals(_timer, null))
{
_timer.Stop();
_timer.Start();
}
}
}
Image.cs:
namespace TMD_Display.Models
{
public class Image
{
public int Id { get; set; }
//public string Name { get; set; }
//public DateTime FirstAppearance { get; set; }
public string ImgPath { get; set; }
}
}
Although your Binding code is incomplete, but I judged that the problem was not in the binding code. You might not know that UWP app has application data folder and installation folder. If you put the images in the 'Assets/Images/' folder of the projects, it actually is installation folder. Then, if you want to get files from there. You need to use the "ms-appx:///" prefix in the URI.
So, if you get the ImgPath value from your database like this '/Images/1.jpg'. In your code, you should add ms-appx:///Assets/ to it.
Please check my following code sample for reference:
<FlipView x:Name="FlipView">
<FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImgPath}"></Image>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
public sealed partial class MainPage : Page
{
public ObservableCollection<Test> tests { get; set; }
public MainPage()
{
this.InitializeComponent();
string path = "/Images/animals.jpg";
tests = new ObservableCollection<Test>() { new Test() {ImgPath="ms-appx:///Assets/"+path } };
FlipView.ItemsSource = tests;
}
}
public class Test
{
public String ImgPath { get; set; }
}
I am trying to add multiple pushpins to a bing map in a windows 10 app. I face the trouble that it is only the last added lat, long that gets added. Let me post my code:
map.xaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Hamburger_Heaven_Challenge"
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="Hamburger_Heaven_Challenge.Map"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Maps:MapControl x:Name="MyMap" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="700" Width="1260">
</Maps:MapControl>
</Grid>
map.xaml.cs:
public sealed partial class Map : Page
{
PushPin pushPin = new PushPin();
public Map()
{
this.InitializeComponent();
AddPushPins();
AddIcon();
MyMap.Center = new Geopoint(new BasicGeoposition() {Latitude = 46.8442643, Longitude = 2.5992004 });
MyMap.ZoomLevel = 6;
}
public void AddPushPins()
{
pushPin.AddPushPin(46.8442643, 2.5992004);
pushPin.AddPushPin(48.873121, 2.374912);
}
public void AddIcon()
{
MapIcon myIcon = new MapIcon();
myIcon.NormalizedAnchorPoint = new Point(0.5, 1.0);
myIcon.Title = "Apartment here";
MyMap.MapElements.Add(myIcon);
for (int i = 0; i < pushPin.Items().Count; i++)
{
myIcon.Location = pushPin.MyGeopoint(i);
}
}
}
pushpin.cs:
internal class PushPin
{
private ObservableCollection<Geopoint> items;
public PushPin()
{
items = new ObservableCollection<Geopoint>();
}
public void AddPushPin(double latitude, double longitude)
{
items.Add(new Geopoint(new BasicGeoposition() { Latitude = latitude, Longitude = longitude }));
}
public Geopoint MyGeopoint(int i)
{
return items[i];
}
public ObservableCollection<Geopoint> Items()
{
return items;
}
}
I think my problem is that i constantly override the mapicon i previously created but how do i get around this?
Any point in the right direction would be appreciated!
Ps. I have tried with binding to an ObservableCollection but we havent been tought enough about databinding for me to figure it out.
Some time ago I had this issue and I found a solution(with MVVM), even more powerfull as it's not limiting You to adding Pushpins only.
At first Create ObservableCollection of UiElements(don't forget to implement property changed).
private ObservableCollection<UIElement> mapElements = new ObservableCollection<UIElement>();
public ObservableCollection<UIElement> MapElements
{
get
{
return mapElements;
}
set
{
mapElements = value;
OnPropertyChanged("MapElements");
}
}
Next create a MapItemsControl inside Your map and bind its ItemsSource to collection You've just made.
<Maps:Map [...]>
<Maps:MapItemsControl ItemsSource="{Binding MapElements}"/>
</Maps:Map>
Finally the only thing You need to do is to add Your Pushpins to MapElements Collection.
public void AddPushpinToTheMap(double longitude, double latitude)
{
var pin = new Pushpin();
pin.Location = new Location(latitude, longitude);
MapElements.Add(pin);
}
That's it the pin will be visible on the map.
The problem was more simple than I imagined. I was a dummy tbh.
public void AddIcon()
{
for (int i = 0; i < pushPin.Items().Count; i++)
{
MapIcon myIcon = new MapIcon();
myIcon.NormalizedAnchorPoint = new Point(0.5, 1.0);
myIcon.Title = "Apartment here";
MyMap.MapElements.Add(myIcon);
myIcon.Location = pushPin.MyGeopoint(i);
}
}
I'm using amCharts-Quick-Charts to display piechart data but I cannot make it display on a pivot screen. The whole screen is just blank when I execute the program, is anyone able to help?
XAML code
<phone:PivotItem Header="Pie Chart">
<Grid>
<amq:PieChart x:Name="pie1"
TitleMemberPath="title"
ValueMemberPath="value"
>
</amq:PieChart>
</Grid>
</phone:PivotItem>
XAML.CS
namespace Project
{
public partial class Humidity : PhoneApplicationPage
{
public Humidity()
{
InitializeComponent();
}
public ObservableCollection<PData> Data = new ObservableCollection<PData>()
{
new PData() { title = "slice #1", value = 30 },
new PData() { title = "slice #2", value = 60 },
new PData() { title = "slice #3", value = 40 },
new PData() { title = "slice #4", value = 10 },
};
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
pie1.DataSource = Data;
}
}
public class PData
{
public string title { get; set; }
public double value { get; set; }
}
}
Looks like you copied the code from AmCharts-in-Windows-Phone? The project and code works just fine. My guess is you pasted the code incorrectly.
Put a break point at
pie1.DataSource = Data; // put a break point here
Does it break at that line?
If NO then you need to hook up the PhoneApplicationPage_Loaded by clicking on the event icon in the designer and setting the Loaded event to that specific function.
If YES then make sure nothing is covering and overriding the Chart inside your XAML.
Pivot Page example
<Grid x:Name="LayoutRoot">
<phone:Pivot>
<phone:PivotItem Header="one">
<amq:PieChart x:Name="pie1"
TitleMemberPath="title"
ValueMemberPath="value">
</amq:PieChart>
</phone:PivotItem>
<phone:PivotItem Header="two"></phone:PivotItem>
<phone:PivotItem Header="three"></phone:PivotItem>
</phone:Pivot>
</Grid>
If all is good it should look like this
I'm working on this surface project where we have a bing maps control and where we would like to draw polylines on the map, by using databinding.
The strange behaviour that's occuring is that when I click the Add button, nothing happens on the map. If I move the map little bit, the polyline is drawn on the map. Another scenario that kind of works, is click the add button once, nothing happens, click it again both polylines are drawn. (In my manual collection I have 4 LocationCollections) so the same happens for the 3rd click and the fourth click where again both lines are drawn.
I have totally no idea where to look anymore to fix this. I have tried subscribing to the Layoutupdated events, which occur in both cases. Also added a collectionchanged event to the observablecollection to see if the add is triggered, and yes it is triggered. Another thing I tried is changing the polyline to pushpin and take the first location from the collection of locations in the pipelineviewmodel, than it's working a expected.
I have uploaded a sample project for if you want to see yourself what's happening.
Really hope that someone can point me in the right direction, because i don't have a clue anymore.
Below you find the code that i have written:
I have the following viewmodels:
MainViewModel
public class MainViewModel
{
private ObservableCollection<PipelineViewModel> _pipelines;
public ObservableCollection<PipelineViewModel> Pipes
{
get { return _pipelines; }
}
public MainViewModel()
{
_pipelines = new ObservableCollection<PipelineViewModel>();
}
}
And the PipelineViewModel which has the collection of Locations which implements INotifyPropertyChanged:
PipelineViewModel
public class PipelineViewModel : ViewModelBase
{
private LocationCollection _locations;
public string Geometry { get; set; }
public string Label { get; set; }
public LocationCollection Locations
{
get { return _locations; }
set
{
_locations = value;
RaisePropertyChanged("Locations");
}
}
}
My XAML looks like below:
<s:SurfaceWindow x:Class="SurfaceApplication3.SurfaceWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="http://schemas.microsoft.com/surface/2008"
xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF"
Title="SurfaceApplication3">
<s:SurfaceWindow.Resources>
<DataTemplate x:Key="Poly">
<m:MapPolyline Locations="{Binding Locations}" Stroke="Black" StrokeThickness="5" />
</DataTemplate>
</s:SurfaceWindow.Resources>
<Grid>
<m:Map ZoomLevel="8" Center="52.332074,5.542302" Name="Map">
<m:MapItemsControl Name="x" ItemsSource="{Binding Pipes}" ItemTemplate="{StaticResource Poly}" />
</m:Map>
<Button Name="add" Width="100" Height="50" Content="Add" Click="add_Click"></Button>
</Grid>
</s:SurfaceWindow>
And in our codebehind we are setting up the binding and the click event like this:
private int _counter = 0;
private string[] geoLines;
private MainViewModel _mainViewModel = new MainViewModel();
/// <summary>
/// Default constructor.
/// </summary>
public SurfaceWindow1()
{
InitializeComponent();
// Add handlers for window availability events
AddWindowAvailabilityHandlers();
this.DataContext = _mainViewModel;
geoLines = new string[4]{ "52.588032,5.979309; 52.491143,6.020508; 52.397391,5.929871; 52.269838,5.957336; 52.224435,5.696411; 52.071065,5.740356",
"52.539614,4.902649; 52.429222,4.801025; 52.308479,4.86145; 52.246301,4.669189; 52.217704,4.836731; 52.313516,5.048218",
"51.840869,4.394531; 51.8731,4.866943; 51.99841,5.122375; 52.178985,5.438232; 51.8731,5.701904; 52.071065,6.421509",
"51.633362,4.111633; 51.923943,6.193542; 52.561325,5.28717; 52.561325,6.25946; 51.524125,5.427246; 51.937492,5.28717" };
}
private void add_Click(object sender, RoutedEventArgs e)
{
PipelineViewModel plv = new PipelineViewModel();
plv.Locations = AddLinestring(geoLines[_counter]);
plv.Geometry = geoLines[_counter];
_mainViewModel.Pipes.Add(plv);
_counter++;
}
private LocationCollection AddLinestring(string shapegeo)
{
LocationCollection shapeCollection = new LocationCollection();
string[] lines = Regex.Split(shapegeo, ";");
foreach (string line in lines)
{
string[] pts = Regex.Split(line, ",");
double lon = double.Parse(pts[1], new CultureInfo("en-GB"));
double lat = double.Parse(pts[0], new CultureInfo("en-GB"));
shapeCollection.Add(new Location(lat, lon));
}
return shapeCollection;
}
I did some digging on this problem and found that there is a bug in the Map implementation. I also made a workaround for it which can be used like this
<m:Map ...>
<m:MapItemsControl Name="x"
behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>
I included this fix in your sample application and uploaded it here: SurfaceApplication3.zip
The visual tree for each ContentPresenter looks like this
When you add a new item to the collection the Polygon gets the wrong Points initially. Instead of values like 59, 29 it gets something like 0.0009, 0.00044.
The points are calculated in MeasureOverride in MapShapeBase and the part that does the calculation looks like this
MapMath.TryLocationToViewportPoint(ref this._NormalizedMercatorToViewport, location, out point2);
Initially, _NormalizedMercatorToViewport will have its default values (everything is set to 0) so the calculations goes all wrong. _NormalizedMercatorToViewport gets set in the method SetView which is called from MeasureOverride in MapLayer.
MeasureOverride in MapLayer has the following two if statements.
if ((element is ContentPresenter) && (VisualTreeHelper.GetChildrenCount(element) > 0))
{
child.SetView(...)
}
This comes out as false because the ContentPresenter hasn't got a visual child yet, it is still being generated. This is the problem.
The second one looks like this
IProjectable projectable2 = element as IProjectable;
if (projectable2 != null)
{
projectable2.SetView(...);
}
This comes out as false as well because the element, which is a ContentPresenter, doesn't implement IProjectable. This is implemented by the child MapShapeBase and once again, this child hasn't been generated yet.
So, SetView never gets called and _NormalizedMercatorToViewport in MapShapeBase will have its default values and the calculations goes wrong the first time when you add a new item.
Workaround
To workaround this problem we need to force a re-measure of the MapLayer. This has to be done when a new ContentPresenter is added to the MapItemsControl but after the ContentPresenter has a visual child.
One way to force an update is to create an attached property which has the metadata-flags AffectsRender, AffectsArrange and AffectsMeasure set to true. Then we just change the value of this property everytime we want to do the update.
Here is an attached behavior which does this. Use it like this
<m:Map ...>
<m:MapItemsControl Name="x"
behaviors:MapFixBehavior.FixUpdate="True"/>
</m:Map>
MapFixBehavior
public class MapFixBehavior
{
public static DependencyProperty FixUpdateProperty =
DependencyProperty.RegisterAttached("FixUpdate",
typeof(bool),
typeof(MapFixBehavior),
new FrameworkPropertyMetadata(false,
OnFixUpdateChanged));
public static bool GetFixUpdate(DependencyObject mapItemsControl)
{
return (bool)mapItemsControl.GetValue(FixUpdateProperty);
}
public static void SetFixUpdate(DependencyObject mapItemsControl, bool value)
{
mapItemsControl.SetValue(FixUpdateProperty, value);
}
private static void OnFixUpdateChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
MapItemsControl mapItemsControl = target as MapItemsControl;
ItemsChangedEventHandler itemsChangedEventHandler = null;
itemsChangedEventHandler = (object sender, ItemsChangedEventArgs ea) =>
{
if (ea.Action == NotifyCollectionChangedAction.Add)
{
EventHandler statusChanged = null;
statusChanged = new EventHandler(delegate
{
if (mapItemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
mapItemsControl.ItemContainerGenerator.StatusChanged -= statusChanged;
int index = ea.Position.Index + ea.Position.Offset;
ContentPresenter contentPresenter =
mapItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
{
MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
mapLayer.ForceMeasure();
}
else
{
EventHandler layoutUpdated = null;
layoutUpdated = new EventHandler(delegate
{
if (VisualTreeHelper.GetChildrenCount(contentPresenter) == 1)
{
contentPresenter.LayoutUpdated -= layoutUpdated;
MapLayer mapLayer = GetVisualParent<MapLayer>(mapItemsControl);
mapLayer.ForceMeasure();
}
});
contentPresenter.LayoutUpdated += layoutUpdated;
}
}
});
mapItemsControl.ItemContainerGenerator.StatusChanged += statusChanged;
}
};
mapItemsControl.ItemContainerGenerator.ItemsChanged += itemsChangedEventHandler;
}
private static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
MapLayerExtensions
public static class MapLayerExtensions
{
private static DependencyProperty ForceMeasureProperty =
DependencyProperty.RegisterAttached("ForceMeasure",
typeof(int),
typeof(MapLayerExtensions),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsMeasure));
private static int GetForceMeasure(DependencyObject mapLayer)
{
return (int)mapLayer.GetValue(ForceMeasureProperty);
}
private static void SetForceMeasure(DependencyObject mapLayer, int value)
{
mapLayer.SetValue(ForceMeasureProperty, value);
}
public static void ForceMeasure(this MapLayer mapLayer)
{
SetForceMeasure(mapLayer, GetForceMeasure(mapLayer) + 1);
}
}