Issue with the MapElements for UWP Maps control - c#

The funny thing is stack gives me a warning saying "questions with a matching tittle have been closed for being too broad", So I'll try to keep it on point.
What is the issue:
On my map control, I am adding a polyline (maybe 3, depending upon
response from server).
Now when I add MapIcons or any MapElement or a MapItemsControl (basically anything) it doesn't show up. After debugging, I found out even though the MyMap.MapElements.Add(myMapIcon); runs perfectly, the MyMap.MapElements.Count only shows the polyline(s). Only the polyline is added.
So I removed my Polyline and guess what all the above mentioned components they all are added to the MapControl and are visible.
I dug a little deeper and found out that each time I call my MyMap.MapElements.Add(myMapIcon) it calls to add the already added polyline as polylines are added via attached properties and binding.
My Code:
My Polyline creator attached class:
public class PolylineCollection
{
public static readonly DependencyProperty PathDataCollectionProperty =
DependencyProperty.Register("PathDataCollection", typeof(ObservableCollection<IPolylinePath>), typeof(PolylineCollection), new PropertyMetadata(null, OnPathCollectionChanged));
public static void SetPathCollection(UIElement element, ObservableCollection<IPolylinePath> value)
{
element.SetValue(PathDataCollectionProperty, value);
}
public static ObservableCollection<IPolylinePath> GetPathCollection(UIElement element)
{
return (ObservableCollection<IPolylinePath>)element.GetValue(PathDataCollectionProperty);
}
private static void OnPathCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e?.NewValue != null)
{
if (e.NewValue is IEnumerable<IPolylinePath> polylineCollection)
{
var mapControl = d as MapControl;
if (mapControl == null)
{
throw new InvalidOperationException(
"Polyline.Track property can only be attached to a MapControl!");
}
mapControl.MapElements.Clear();
foreach (var polyline in GetPathCollection(mapControl))
{
mapControl.MapElements.Add(CreateMapPolyline(polyline));
}
}
}
}
private static MapPolyline CreateMapPolyline(IPolylinePath track)
{
var polyline = new MapPolyline()
{
StrokeColor = track.PolylineColor.Color,
StrokeThickness = track.PolylineThinkness,
StrokeDashed = false,
Path = track.PolylineGeopath,
};
if (track.PolylineColorMode == Enums.PolylineColorMode.Selected)
polyline.ZIndex = 5;
else
polyline.ZIndex = 1;
return polyline;
}
The PolylineColorMode is a quick enum to help me find out which polyline is selected and perform operations please ignore it.
My XAML:
<maps:MapControl x:Name="MyMap" extentions:PolylineCollection.PathCollection="{x:Bind ViewModel.PolylinePoints,Mode=OneWay}">
Where the extentions is a namespace where the above class (polylineCollection) resides.
The CodeBehind:
MapIcon errorIcon = new MapIcon()
{
Location = ViewModel.CurrentDriftLocation,
NormalizedAnchorPoint = new Windows.Foundation.Point(0.5, 1.0),
Title = "Drifted",
ZIndex = 3,
Image = Windows.Storage.Streams.RandomAccessStreamReference.CreateFromUri(new System.Uri("ms-appx:///SharedAssets/ErrorAnalysysAssets/DriftPoint.png"))
};
MyMap.MapElements.Add(errorIcon);
The ViewModelBacking property For Polyline
private ObservableCollection<Extentions.Map.Extentions.IPolylinePath> polylinePoints;
public ObservableCollection<Extentions.Map.Extentions.IPolylinePath> PolylinePoints
{
get { return polylinePoints; }
set { polylinePoints = value; RaisePropertyChanged(nameof(PolylinePoints)); }
}
The IPolylinePath
public interface IPolylinePath
{
SolidColorBrush PolylineColor { get; }
int PolylineThinkness { get; set; }
string PolylineTag { get; set; }
IEnumerable<BasicGeoposition> PolylinePoints { get; set; }
Geopath PolylineGeopath { get; }
PolylineColorMode PolylineColorMode { get; set; }
}
Can anyone tell me why does this happen? or how can I handle it so that my mapControl can show other elements as well?

Related

How have a custom UICollectionViewCell fill horizontal width?

I'm trying to make an app for people of my student dancing association to find a dance partner. It's been a fun project during corona. Everything is done in ASP Core 5 and Xamarin Forms, and it's almost ready for testing. However, I'm having some trouble with the chat UI. When I use a CollectionView in Forms, it loads very slowly, due to rendering performance (at least on Android, iOS is generally much faster). To solve that I tried creating it as a view and use native renderers for iOS and Android. With Android, I simply used a NuGet package (XamarinLibrary.Xamarin.AndroidX.ChatKit), but for iOS I could find no such package. There's a native SwiftUI package (MessageKit), though I don't know how to wrap that (and the documentation is a lot). So I started to instead write it in Xamarin iOS. My test loads really quickly, although I'm now struggling with the layout of the Collection and the ViewCell. Could someone show me how to create message cells that fill the horizontal space?
Thanks in advance! :)
This is what it looks like right now:
Screenshot
[UPDATE] I partially solved the issue by adding this (though it does not work in the constructor):
Screenshot 2
public override bool ShouldInvalidateLayoutForBoundsChange(CGRect newBounds) {
MinimumLineSpacing = 10;
MinimumInteritemSpacing = 10;
SectionInset = new UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10);
ItemSize = new CGSize(width: (newBounds.Width - 20), height: 100);
return true;
}
My code currently (the data source isn't correct yet):
public class MessageListLayout : UICollectionViewFlowLayout {
public MessageListLayout() {
ItemSize = new CGSize(UIScreen.MainScreen.Bounds.Size.Width, 50);
EstimatedItemSize = AutomaticSize;
}
public override bool ShouldInvalidateLayoutForBoundsChange(CGRect newBounds) {
return true;
}
public override UICollectionViewLayoutAttributes LayoutAttributesForItem(NSIndexPath path) {
return base.LayoutAttributesForItem(path);
}
public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect(CGRect rect) {
return base.LayoutAttributesForElementsInRect(rect);
}
}
public class MessageListDataSource : UICollectionViewDataSource {
private string CellId { get; set; }
#region Computed Properties
public UICollectionView CollectionView { get; set; }
public List<int> Numbers { get; set; } = new List<int>();
#endregion
#region Constructors
public MessageListDataSource(UICollectionView collectionView, string cellId) {
// Initialize
CollectionView = collectionView;
CellId = cellId;
// Init numbers collection
for (int n = 0; n < 100; ++n) {
Numbers.Add(n);
}
}
#endregion
#region Override Methods
public override nint NumberOfSections(UICollectionView collectionView) {
// We only have one section
return 1;
}
public override nint GetItemsCount(UICollectionView collectionView, nint section) {
// Return the number of items
return Numbers.Count;
}
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) {
// Get a reusable cell and set {~~it's~>its~~} title from the item
var cell = collectionView.DequeueReusableCell(CellId, indexPath) as MessageListViewCell;
cell.Text = Numbers[(int)indexPath.Item].ToString();
return cell;
}
public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath) {
// We can always move items
return true;
}
public override void MoveItem(UICollectionView collectionView, NSIndexPath sourceIndexPath, NSIndexPath destinationIndexPath) {
// Reorder our list of items
var item = Numbers[(int)sourceIndexPath.Item];
Numbers.RemoveAt((int)sourceIndexPath.Item);
Numbers.Insert((int)destinationIndexPath.Item, item);
}
#endregion
}
So this should be the base message bubble.
public class MessageListViewCell : UICollectionViewCell {
UIImageView imageView;
public string Text { get; set; }
[Export("initWithFrame:")]
public MessageListViewCell(CGRect frame) : base(frame) {
BackgroundView = new UIView { BackgroundColor = UIColor.Orange };
SelectedBackgroundView = new UIView { BackgroundColor = UIColor.Green };
ContentView.Layer.BorderColor = UIColor.LightGray.CGColor;
ContentView.Layer.BorderWidth = 2.0f;
ContentView.BackgroundColor = UIColor.White;
//ContentView.Transform = CGAffineTransform.MakeScale(1, 1);
ContentView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
imageView = new UIImageView(UIImage.FromBundle("placeholder.png"));
imageView.Center = ContentView.Center;
imageView.Transform = CGAffineTransform.MakeScale(0.7f, 0.7f);
ContentView.AddSubview(imageView);
}
public UIImage Image {
set {
imageView.Image = value;
}
}
[Export("custom")]
public void Custom() {
// Put all your custom menu behavior code here
Console.WriteLine("custom in the cell");
}
public override bool CanPerform(Selector action, NSObject withSender) {
if (action == new Selector("custom"))
return true;
else
return false;
}
}
[assembly: ExportRenderer(typeof(MessagesListControl), typeof(MessagesListControlRenderer))]
namespace Vinder.iOS.UI.Renderers {
public class MessagesListControlRenderer : ViewRenderer<MessagesListControl, UICollectionView> {
static readonly NSString messageListViewCell = new("MessageListViewCell");
private UICollectionView collectionView { get; set; }
protected override void OnElementChanged(ElementChangedEventArgs<MessagesListControl> e) {
base.OnElementChanged(e);
if (e.OldElement != null) {
// Unsubscribe to events
}
if (e.NewElement != null) {
if (Control == null) {
collectionView = new UICollectionView(new CGRect(0, 0, UIScreen.MainScreen.Bounds.Size.Width, 300), new MessageListLayout());
collectionView.RegisterClassForCell(typeof(MessageListViewCell), messageListViewCell);
collectionView.BackgroundColor = UIColor.Blue; // blue is easy for seeing the correct bounds
collectionView.DataSource = new MessageListDataSource(collectionView, messageListViewCell);
SetNativeControl(collectionView);
}
// Subscribe to events
}
}
}
}

How to add custom images in a entry (used for a chat) in Xamarin forms macos?

I am trying to create an entry that supports the addition of both text but also images (custom ones). Right now i am trying to achieve this in macOS.
What I have gone ahead and done so far is to create a custom control, where i have a List of images, that i add to if a new image is added to the entry. Once added, the idea is for it to go on the right side of the last text (or image).
Control:
public class ChatEntry : Entry
{
public ChatEntry()
{
this.HeightRequest = 50;
}
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Images), typeof(List<string>), typeof(ChatEntry), string.Empty);
public static readonly BindableProperty LineColorProperty =
BindableProperty.Create(nameof(LineColor), typeof(Xamarin.Forms.Color), typeof(ChatEntry), Color.White);
public static readonly BindableProperty ImageHeightProperty =
BindableProperty.Create(nameof(ImageHeight), typeof(int), typeof(ChatEntry), 40);
public static readonly BindableProperty ImageWidthProperty =
BindableProperty.Create(nameof(ImageWidth), typeof(int), typeof(ChatEntry), 40);
public Color LineColor
{
get { return (Color)GetValue(LineColorProperty); }
set { SetValue(LineColorProperty, value); }
}
public int ImageWidth
{
get { return (int)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public int ImageHeight
{
get { return (int)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public List<string> Images
{
get { return (List<string>)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
MacOS renderer:
[assembly: ExportRenderer(typeof(ChatEntry), typeof(ChatEntryRenderer))]
namespace Project.MacOS.Renderers
{
public class ChatEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || e.NewElement == null)
return;
var element = (ChatEntry)this.Element;
var textField = this.Control;
if (element.Images != null)
{
// TODO : Logic GetImageView
}
CALayer bottomBorder = new CALayer
{
Frame = new CGRect(0.0f, element.HeightRequest - 1, this.Frame.Width, 1.0f),
BorderWidth = 2.0f,
BorderColor = element.LineColor.ToCGColor()
};
textField.Layer.AddSublayer(bottomBorder);
textField.Layer.MasksToBounds = true;
}
private NSView GetImageView(string imagePath, int height, int width)
{
var uiImageView = new NSImageView()
{
Frame = new RectangleF(0, 0, width, height)
};
uiImageView.Image = NSImage.ImageNamed(imagePath);
NSView objLeftView = new NSView(new System.Drawing.Rectangle(0, 0, width + 10, height));
objLeftView.AddSubview(uiImageView);
return objLeftView;
}
}
}
Issue i have now is to bind this all together. I have created a GetImageView where I can get in return a NSView, but how i incorporate this so that it places on the right side of the prior character (or image), i am unsure and would need some guidance.
Before everything, I'd like to say that Xamarin for MacOS is still in preview, for Entry, there's still some features that pending development and may affect your app, you should consider and test carefully if you're gonna release your app. You can find status for entry here
If I understood your problem correctly, you're trying to create a custom entry that can put both texts and images inside like this:
text[IMG1]abc[IMG2]...
and here's something that might give some ideas: how to implement a label with a image in the center.
It's leveraging NSTextAttachment to hold the image and NSMutableAttributedString]4 to hold the entire string in entry. The difference is that, it only have one image, but you're going to have several images in the entry, so you'll probably need a dictionary to map the placeholder in the string with the actual image.

I am having difficulty rendering a WPF ArcSegment. The Stroke color is not working

I am attempting to render part of a model as a semicircle using ArcSegment. My code uses a number of 2D Path based objects. All are working except the module that uses ArcSegment. For example a similar module using LineSegment works correctly.
I can see that the basic geometry of my ArcSegment is correct as the Fill property makes the arc visible by fillng the area inside the expected semicircle.
I am not using XAML. All shapes are built using C# / WPF functions.
Following is the code that renders all of my Geometry objects using a WPF Path object:
// builder constructs a collection of Geometry objects and returns the in a GeometryGroup via ToGeometry()
GeometryGroup model2D = builder.ToGeometry();
model2D.Transform = BuildLocalTransform2D(visualModel);
var visual2D = new System.Windows.Shapes.Path();
if (typeof(Foundation.Visualisation.Elements2D.ShapeElement).IsAssignableFrom(visualModel.GetType()))
visual2D.Fill = ((Foundation.Visualisation.Elements2D.ShapeElement)visualModel).ShapeStyle.BackgroundBrush;
else
// ArcSegment and LineSegment based geometries always follow this path
// was using transparent but needed to add a color to make ArcSegment based Path visible
// the assignment to Stroke below does not work (line remains transparent) when model2D contains a PathGeometry that contains one ArcSegment
// I can see that the arc has the correct shape when the fill is not transparent
//visual2D.Fill = Brushes.Transparent;
visual2D.Fill = Brushes.Cyan;
// debug shows StrokeThickness is always 2
visual2D.StrokeThickness = ((PathElement)visualModel).LineStyle.LineThickness;
// debug shows that Stroke is always Brushes.Black
visual2D.Stroke = ((PathElement)visualModel).LineStyle.LineBrush;
visual2D.StrokeStartLineCap = PenLineCap.Round;
visual2D.StrokeEndLineCap = PenLineCap.Round;
visual2D.StrokeLineJoin = PenLineJoin.Round;
visual2D.Data = model2D;
WpfVisual2DProxy node = new WpfVisual2DProxy(visual2D, visualModel);
visualModel.AddProxy(node);
// the following adds the visual to an ItemsControl using a Canvas based template:
// this.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(Canvas)));
((VisualContainer)((WpfVisual2DProxy)parentNode).Visual2D).AddChild(visual2D);
Following is the code I use to create the ArcSegment:
public void AddArc(ModelPoint2D arcStart, ModelPoint2D arcEnd, double ellipseFactor, double degrees, bool isLarge, SweepDirection sweepDirection, LineVisualStyle lineStyle)
{
double radius = ellipseFactor * arcStart.Distance(arcEnd) / 2.0;
List<ArcSegment> list = new List<ArcSegment>(2);
Point start = converter.ToPoint2D(arcStart);
list.Add(new ArcSegment(converter.ToPoint2D(arcEnd), new Size(radius, radius), Extrusion.DegreesToRadians(degrees), isLarge, SweepDirection.Clockwise, false));
var pathFigure = new PathFigure(start, list, false);
var pfList = new List<PathFigure>(1);
pfList.Add(pathFigure);
group.Children.Add(new PathGeometry(pfList));
}
The geometry produced here always has a transparent stroke but the arc can be made visible using the Fill property.
Below is the code I used to create a rectangle equiv of the semicircle:
public void AddPath(IList<ModelPoint2D> Points, LineVisualStyle lineStyle)
{
List<LineSegment> list = new List<LineSegment>(Points.Count);
Point start = converter.ToPoint2D(Points[0]);
for (int i = 1; i < Points.Count; i++)
{
list.Add(new LineSegment(converter.ToPoint2D(Points[i]), true));
}
var pathFigure = new PathFigure(start, list, false);
var pfList = new List<PathFigure>(1);
pfList.Add(pathFigure);
group.Children.Add(new PathGeometry(pfList));
}
The code using LineSegment produces a Geometry that responds to both Stroke and Fill Settings.
The resultant render is shown below:
The Semicircle at the top has Fill visible but not Stroke
I have tried many combinations including Adding the PathGeometry directly to Path.Data rather than wrapped in a GeometryGroup.
Any suggestions / assistance would be much appreciated.
The requested VisualModel code is below:
public abstract class VisualModel
{
/// <summary>
/// Recursive hierarchy of VisualModel objects that describe the visual structure of an entity for a specific ViewportType.
/// The top level of the hierarchy is owned by EntityViews via a ViewportTypeVisualModel entry
/// </summary>
/// <param name="id">An optional model component identifier - useful for debug</param>
public VisualModel(string id)
{
domainBindings = null;
proxies = null;
Id = id;
ParentVisualModel = null;
LocalTransforms = null;
IsStatic = true;
}
public bool HasDomainBindings
{
get
{
if (domainBindings == null)
return false;
return domainBindings.Count > 0;
}
}
public bool HasProxies
{
get
{
if (proxies == null)
return false;
return proxies.Count > 0;
}
}
public string Id { get; private set; }
/// <summary>
/// Static objects are not affected by transforms that change after the render is constructed.
/// Non-Static (Dynamic) objects have transforms that may be changed by the application after the render is constructed
/// Dynamic objects have attached event handlers that adjust the transforms in response to application events
/// </summary>
public bool IsStatic { get; private set; }
public List<TransformBase> LocalTransforms { get; private set; }
public VisualModel ParentVisualModel { get; internal set; }
public void AddDomainBinding(BindingBase binding)
{
DomainBindings.Add(binding);
}
public void AddLocalTransform(TransformBase transform)
{
if (LocalTransforms == null)
LocalTransforms = new List<TransformBase>(4);
LocalTransforms.Add(transform);
if (transform.IsDynamic)
IsStatic = false;
}
public void AddProxy(VisualProxy visualProxy)
{
Proxies.Add(visualProxy);
}
protected List<BindingBase> DomainBindings
{
get
{
if (domainBindings == null)
domainBindings = new List<BindingBase>();
return domainBindings;
}
}
protected List<VisualProxy> Proxies
{
get
{
if (proxies == null)
proxies = new List<VisualProxy>();
return proxies;
}
}
public virtual void UnbindAll()
{
if (domainBindings == null)
return;
foreach (BindingBase binding in this.DomainBindings)
binding.UnBind();
}
private List<BindingBase> domainBindings;
private List<VisualProxy> proxies;
}
Note that VisualModel represents a graphic / render technology agnostic model container. The VisualProxy objects it contains are the base class for technology specific graphics objects. In this case WpfVisual2DProxy objects wrap WPF UIElement objects that contain my WPF renderable representations.
Following is VisualProxy:
public abstract class VisualProxy
{
public VisualModel.VisualModel VisualModel { get; private set; }
public VisualProxy Parent { get; private set; }
public VisualProxy(VisualModel.VisualModel visualModel)
{
VisualModel = visualModel;
Parent = null;
}
public VisualProxy(VisualModel.VisualModel visualModel, VisualProxy parent)
{
VisualModel = visualModel;
Parent = parent;
}
}
And following is WpfVisual2DProxy:
public class WpfVisual2DProxy : VisualProxy
{
public UIElement Visual2D { get; private set; }
public WpfVisual2DProxy(UIElement visual2D, VisualModel visualModel) : base(visualModel)
{
Visual2D = visual2D;
}
public WpfVisual2DProxy(UIElement visual2D, VisualModel visualModel, WpfVisual2DProxy parent) : base(visualModel, parent)
{
Visual2D = visual2D;
}
}
Many thanks to all who turned their minds to my problem.
Clemens is correct. I did not have isStroked set to true on my ArcSegment.
Embarrassingly simple.
Thank you

Edit text in Listview Custom Adapter Loses its position while scrolling? - c# - Xamarin.Android

When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
namespace Kites
{
public class Marks
{
// add any if you need more
public string StudentName { get; set; }
public string MarksScored { get; set; }
}
public class TEXTCHECK
{
public int POS { get; set; }
public string Value { get; set; }
}
public class MarksListViewAdapter : BaseAdapter<Marks>
{
private List<Marks> mstuduentmarks;
private List<TEXTCHECK> abc = new List<TEXTCHECK>();
private Context mcontext;
public MarksListViewAdapter (Context context, List<Marks> stud)
{
mstuduentmarks = stud;
mcontext = context;
}
public override int Count
{
get
{
return mstuduentmarks.Count;
// return mattendence.Count;
}
}
public override long GetItemId (int position)
{
return position;
}
public override Marks this[int position]
{
get
{
return mstuduentmarks [position];
// return mattendence [position];
}
}
class ViewHolder : Java.Lang.Object
{
public EditText comsevin;
public TextView namenmn;
}
public override View GetView (int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
View view = convertView;
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;
view.Tag = holder;
}
else
{
holder = (ViewHolder)view.Tag;
}
holder.namenmn.Text = mstuduentmarks[position].StudentName;
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
//TextView txtStudent =
//txtStudent.Text = mstuduentmarks[position].StudentName;
//txtMarks.FocusChange += (object sender, View.FocusChangeEventArgs e) =>
//{
// //txtMarks.RequestFocusFromTouch ();
// mstuduentmarks[position].MarksScored = txtMarks.Text;
//};
holder.comsevin.BeforeTextChanged += (sender, e) =>
{
abc.Add(new TEXTCHECK { POS = position, Value = mstuduentmarks[position].MarksScored });
};
holder.comsevin.AfterTextChanged += (sender, e) =>
{
int a = abc[pos].POS;
mstuduentmarks[pos].MarksScored = abc[pos].Value;
};
//txtMarks.Tag = position;
//txtMarks.TextChanged += TxtMarks_TextChanged;
return view;
}
//void TxtMarks_TextChanged (object sender, Android.Text.TextChangedEventArgs e)
//{
// EditText txtMarks = (EditText)sender;
// //var position = (int)txtMarks.Tag;
//}
}
}
When we enter value in row 1 the value entered in row 1 is appearing back in row 6 when we scroll to the row 6. Please see the below code and advice.
As a rule of thumb, when experiencing lists that don't reflect the dataset (experiencing item repetition for example) in listview / recyclerview it means that you're either using dirty views which were previously used and then uncorrectly Re-Bound, or simply using wrong positions during bind
I see where you are getting it wrong:
if (view == null) // otherwise create a new one
{
view = LayoutInflater.From(mcontext).Inflate(Resource.Layout.listview_Marks, null, false);
holder = new ViewHolder();
holder.comsevin = view.FindViewById<EditText>(Resource.Id.editTextTeacherMarks);
holder.namenmn = view.FindViewById<TextView>(Resource.Id.textStudentNameTeacherMarks);
holder.namenmn.Tag = position;//<------------here!!!
view.Tag = holder;
}
TLDR Don't save positions this way.
Whats happening: this instance of your view is being reused by listView, meaning that sometimes (many times) if (view == null) will be false and this means Tag property will not be updated for row 6 (or any other calls that will use recycled Views) and you are in fact using a dirty value.
You are then trying to use the Tag property as position, but forgetting this tag is already dirty if the view was recycled
int pos = (int)holder.namenmn.Tag;
holder.comsevin.TextChanged += (sender, e) =>
{
abc[pos].Value = holder.comsevin.Text;
};
Since you have access to the position in this method call you should use it directly
take a look at this guide from Java Code geeks even though it's in Java you will be able to see a good implementation of the old ViewHolder/ListView pattern.
Hope this helps

WPF/C# entirely programmatically binding an array of objects to a static ObservableCollection

Please assume this entire question deals in code, without any XAML.
I have a static ObservableCollection named myStaticList. It's a part of a non-static class named myClass.
public class myClass
{
public static ObservableCollection<CheckBoxStructure> myStaticList { get; set; }
static myClass()
{
myStaticList = new ObservableCollection<CheckBoxStructure>();
}
}
And the definition of CheckBoxStructure:
public class CheckBoxStructure
{
public string Description { get; set; }
public bool IsSelected { get; set; }
}
In addition, there's an array of checkboxes called checkBoxArray[], holding 3 elements. each checkbox has as content a textbox.
What I want to do is programmatically bind (two-way) these two, in such a manner that the IsChecked property of the checkboxes in the checkBoxArray[] array will bind to the IsSelected property of the myStaticList's CheckBoxStructure, and similarly so between the text of the textboxes inthe checkboxes' content and the Description property of the myStaticList's CheckBoxStructure.
In addition, I would like to avoid using loops, since it is preferable that this two lists will update each other if they change in size.
How is this possible?
Thanks!
Using XAML, an easy way would be to the declare an ItemsControl and a DataTemplate for it so that you can have a UserControl (CheckBox and TextBox inside) with its DataContext being a CheckBoxStructure. This way the bindings work between CheckBox.IsChecked and IsSelected property and between TextBox.Text and Description property.
If you need to this only in code then you would have to create same behavior (ItemsControl with a DataTemplate). You have at least 2 options
1.
DataTemplate template = new DataTemplate();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
template.VisualTree = factory;
FrameworkElementFactory childFactory = new FrameworkElementFactory(typeof(CheckBox));
childFactory.SetBinding(CheckBox.IsChecked, new Binding("IsSelected"));
factory.AppendChild(childFactory);
childFactory = new FrameworkElementFactory(typeof(TextBox));
childFactory.SetBinding(Label.ContentProperty, new Binding("Description"));
factory.AppendChild(childFactory);
2.
MemoryStream sr = null;
ParserContext pc = null;
string xaml = string.Empty;
xaml = "<DataTemplate><StackPanel><TextBlock Text="{Binding Description"/><CheckBox IsChecked="{Binding IsSelected"/></StackPanel></DataTemplate>";
sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
DataTemplate datatemplate = (DataTemplate)XamlReader.Load(sr, pc);
this.Resources.Add("dt", datatemplate);
Later edit, after discussion from comments; this example works only one way of binding but is easily to make it two ways. Please note that this is only a trivial example of a concept and is not complete: you need to modify the list classes to suit how you wish for objects to be paired, you may need to add more guards for corner cases, you may need to make it thread safe and so on...
First the basic binding objects:
class Binder
{
public Binder()
{
_bindings = new Dictionary<string, List<string>>();
}
private INotifyPropertyChanged _dataContext;
public INotifyPropertyChanged DataContext
{
get { return _dataContext; }
set
{
if (_dataContext != null)
{
_dataContext.PropertyChanged -= _dataContext_PropertyChanged;
}
_dataContext = value;
_dataContext.PropertyChanged += _dataContext_PropertyChanged;
}
}
void _dataContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_bindings.ContainsKey(e.PropertyName))
{
var bindableType = _dataContext.GetType();
var bindableProp = bindableType.GetProperty(e.PropertyName);
if (bindableProp == null)
{
return;
}
var binderType = this.GetType();
foreach (var binderPropName in _bindings[e.PropertyName])
{
var binderProp = binderType.GetProperty(binderPropName);
if (binderProp == null)
{
continue;
}
var value = bindableProp.GetValue(_dataContext);
binderProp.SetValue(this, value);
}
}
}
Dictionary<string, List<string>> _bindings;
public void AddBinding(string binderPropertyName, string bindablePropertyName)
{
if (!_bindings.ContainsKey(bindablePropertyName))
{
_bindings.Add(bindablePropertyName, new List<string>());
}
_bindings[bindablePropertyName].Add(bindablePropertyName);
}
}
class Bindable : INotifyPropertyChanged
{
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then the holding lists for them:
class BindableList<T> : List<T> where T : Bindable
{
public event Action<T> ItemAdded;
public new void Add(T item)
{
base.Add(item);
NotifyItemAdded(item);
}
private void NotifyItemAdded(T item)
{
if (ItemAdded != null)
{
ItemAdded(item);
}
}
}
class BinderList<T> : List<T> where T : Binder
{
public BinderList()
{
_bindingRules = new Dictionary<string, string>();
}
private BindableList<Bindable> _dataContextList;
public BindableList<Bindable> DataContextList
{
get { return _dataContextList; }
set
{
if (_dataContextList != null)
{
_dataContextList.ItemAdded -= _dataContextList_ItemAdded;
}
_dataContextList = value;
_dataContextList.ItemAdded += _dataContextList_ItemAdded;
}
}
void _dataContextList_ItemAdded(Bindable obj)
{
foreach (var pair in _bindingRules)
{
this[Count-1].AddBinding(pair.Key, pair.Value);
this[Count - 1].DataContext = obj;
}
}
private Dictionary<string, string> _bindingRules;
public void AddBindingRule(string binderPropertyName, string bindablePropertyName)
{
_bindingRules.Add(binderPropertyName, bindablePropertyName);
}
}
Now the actual classes with properties:
class BinderElement : Binder
{
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
}
class BindableElement : Bindable
{
private string _description;
public string Description
{
get
{
return _description;
}
set
{
_description = value;
NotifyPropertyChanged("Description");
}
}
}
And an example to use them:
static void Main(string[] args)
{
var bindableList = new BindableList<Bindable>();
var binderList = new BinderList<BinderElement>()
{
new BinderElement(),
new BinderElement()
};
binderList.DataContextList = bindableList;
binderList.AddBindingRule("Description", "Description");
bindableList.Add(new BindableElement());
bindableList.Add(new BindableElement());
((BindableElement)bindableList[1]).Description = "This should arrive in BinderElement Description property";
Console.WriteLine(binderList[1].Description);
Console.ReadLine();
}

Categories

Resources