Xamarin highlight cells in listview when cells are being reused - c#

I have a simple listview that shows client name and age. The list needs to be scrolled and I made the rows' background alternate colour (white and blue). But if the cell containing the client's age is 18, I want to highlight in orange and I want to highlight in red if the age is negative (to signal there is an error).
It all works fine until I start scrolling. At that point everything is messed up amd the orange/red background is not applied correctly.
The adapter code is below. While debugging I noticed that the variable position changes value at each iteration. For example if I initially show only 8 rows, after scrolling I see that position goes to 9, 10... then 5, 4... I understand it might be because it is reusing rows but how can I make it work as expected? I hope someone can help since I tried many time but still didn't succeed. Thank you.
class MyListViewAdapter : BaseAdapter<dbClient>
{
public List<dbClient> mItems;
private Context mContext;
private int mRowLayout;
private string[] mAlternatingColors;
// Default constructor
public MyListViewAdapter(Context context, List<dbClient> items, int rowLayout)
{
mItems = items;
mContext = context;
mRowLayout = rowLayout;
mAlternatingColors = new string[] { "#F2F2F2", "#00bfff" };
}
// Tells how many rows are in the dataset
public override int Count
{
get { return mItems.Count; }
}
// Return a row identifier
public override long GetItemId(int position)
{
return position;
}
// Return the data associated with a particular row
public override dbClient this[int position]
{
get { return mItems[position]; }
}
// Return a view for each row
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if(row == null)
{
row = LayoutInflater.From(mContext).Inflate(Resource.Layout.listview_row, null, false);
}
row.SetBackgroundColor(Color.ParseColor(mAlternatingColors[position % mAlternatingColors.Length]));
TextView txtName = row.FindViewById<TextView>(Resource.Id.nameView);
txtName.Text = mItems[position].Name;
TextView txtAge = row.FindViewById<TextView>(Resource.Id.ageView);
txtAge.Text = mItems[position].Age.ToString();
// highlight if aged 18
if(txtAge.Text == "18")
{ txtAge.SetBackgroundColor(Color.Orange); }
// signale there is error in the age reported
if(txtAge.Text.Contains("-"))
{ txtAge.SetBackgroundColor(Color.Red); }
return row;
}
private Color GetColorFromInteger(int color)
{
return Color.Rgb(Color.GetRedComponent(color), Color.GetGreenComponent(color), Color.GetBlueComponent(color));
}
}

It all works fine until I start scrolling. At that point everything is messed up amd the orange/red background is not applied correctly.
You're right that it reuses rows, so in your code, you changed color for different reasons and you didn't change it back to your original color. Just need to change part of your code in GetView for example like this:
// highlight if aged 18
if (txtAge.Text == "18")
{ txtAge.SetBackgroundColor(Color.Orange); }
// signale there is error in the age reported
else if (txtAge.Text.Contains("-"))
{ txtAge.SetBackgroundColor(Color.Red); }
// set back to default color
else
{
txtAge.SetBackgroundColor(Color.Black);
}

Related

How to change a color of each entered word in Avalon Edit WPF

What am i trying to do is "check color of every single entered word" and then change it into color one by one. For example default color is a black (then check color of words if its not a red change it into red but a word by word each of them).
I've tried this but it is changes specific line color, not exactly what am i finding one.
class LineColorizer : DocumentColorizingTransformer
{
int LineNumber;
public LineColorizer(int lineNumber)
{
LineNumber = lineNumber;
}
protected override void ColorizeLine(ICSharpCode.AvalonEdit.Document.DocumentLine line)
{
if (!line.IsDeleted && line.LineNumber == LineNumber)
{
ChangeLinePart(line.Offset, line.EndOffset, ApplyChanges);
}
}
void ApplyChanges(VisualLineElement element)
{
element.TextRunProperties.SetForegroundBrush(Brushes.Red);
}
}
var line = rtb.Document.GetLineByNumber(3);
rtb.TextArea.TextView.LineTransformers.Add(new LineColorizer(3));
How to accomplish something like this?

C# MSChart : Bars are colored only the first time

To show Candlebars on Chart I use this code and it works fine. When I scroll the chart1.Series["SerieVolume"] has the default series color.
Can you explain me please what to do, so that the bars are painted with the good color every time?
private void DrawBarre(int no)
{
chart1.Series["SerieBougie"].Points.AddXY(no,Glob.Barres[no].High, Glob.Barres[no].Low,
Glob.Barres[no].Open, Glob.Barres[no].Close);
chart1.Series["SerieVolume"].Points.AddXY(no, Glob.Barres[no].Volume);
chart1.Series["SerieIndic1"].Points.AddXY(no, Glob.Barres[no].Volume);
chart1.Series["SerieIndic1_100"].Points.AddXY(no, Glob.Barres[no].Volume);
chart1.Series["SerieIndic2"].Points.AddXY(no, Glob.Barres[no].Volume);
chart1.Series["SerieIndic2_100"].Points.AddXY(no, Glob.Barres[no].Volume);
if ( Glob.Barres[no].Open > Glob.Barres[no].Close)
{
chart1.Series["SerieVolume"].Points[no].Color = System.Drawing.Color.MistyRose;
}
else
{
chart1.Series["SerieVolume"].Points[no].Color = System.Drawing.Color.PaleTurquoise;
}
hScrollBar1.Maximum = Glob.Barres.Count - Zoom.val;
}

How do I delete from the list?

I'm kind of new to programming and come to quite of a problem.
I'm sure the solution is quite simple but I just can't get my head around it.
I just want to know how to Delete from the listbox and delete the data from the list entirely, because whenever I delete from the list box it disappears but when I add a new layer all the layers that I have deleted come back (so I'm guessing it doesn't really delete from the list?.
This is at the start of my code at the top:
List<Layer> layers = new List<Layer>();
This is my Layers Class:
public class Layer
{
private Image mLayerData = null;
private string mLayerName = "Layer";
public string LayerName
{
get { return mLayerName; }
set { mLayerName = value; }
}
public Image LayerData
{
get { return mLayerData; }
}
public Layer(int width = 500, int height = 500, string layername = "Layer")
{
mLayerData = new Bitmap(width, height);
mLayerName = layername;
}
}
and this is my addLayer Function:
private void addNewLayer()
{
string layerName = "Layer";
layerName += layers.Count;
// Create a default layer in our stack of layers
layers.Add(new Layer(pictureBox1.Width, pictureBox1.Height, layerName));
// Make the picture box talk to the default layer
pictureBox1.Image = layers[0].LayerData;
pictureBox1.Invalidate();
// Update the list of layers
listLayers.Items.Clear();
foreach(Layer l in layers)
{
listLayers.Items.Add(l.LayerName);
}
listLayers.SelectedIndex = listLayers.Items.Count - 1;
}
for my deleteLayer function I have this:
private void deleteLayer()
{
listlayers.Items.RemoveAt(listlayers.SelectedIndex);
}
A better approach would be to use BindingList, which is supports Data Binding and use DataSource property of Listbox to bind the collection.
For example,
BindingList<Layer> layers = new BindingList<Layer>();
listBox.DataSource = layers;
listBox.DisplayMember = nameof(Layer.LayerName);
Also, note that instead of binding/add names of Layer to the Listbox, you should instead bind the Collection of Layer and use the DisplayMember property to ensure the LayerName is displayed in the Listbox.
Now you could Add to the listbox as the following
var layer = new Layer(pictureBox1.Width, pictureBox1.Height, layerName);
layers.Add(newLayer);
Remove
layers.Remove((Layer)listBox.SelectedItem);
The BindingList would refresh the Listbox by itself.

Make TabControl Width equal to Max TabItems Width and Height

I have a TabControl with many TabItems inside.
I am trying to make TabControl's width equal to Max of its TabItems Width(same for Height).
I cannot set it explicitly, because my program is multilanguage, and so depending on language selected, I need different widths to all of my labels(they all are in "auto").
Is there a way to do in only in XAML?
Edit :
I found a part of solution in order to do it in the code : When I open my window I do the following :
List<TabItem> list = new List<TabItem>();
list.AddRange(tabControl.Items.OfType<TabItem>());
foreach (TabItem tabItem in list)
{
tabControl.SelectedItem=tabItem;
tabControl.UpdateLayout();
tabItem.UpdateLayout();
System.Threading.Thread.Sleep(250);
maxWidth = Math.Max(maxWidth, tabItem.ActualWidth);
maxHeight = Math.Max(maxHeight, tabItem.ActualHeight);
}
tabControl.Width = maxWidth;
tabControl.Height = maxHeight;
It seems to be a working solution, but I don't understand why tabItem.ActualWidth and tabItem.ActualHeight are always set to 0?
To understand better, I put a screenshot of my window :
When I open my window, I already know in which language it may be opened, so the TabItems dimensions don't change after the windows is loaded.
Edit 2 :
Here is concretely what I mean by "size of TabItems differ regarding used language, this is not the best example as here difference is small, but I would like to apply that to all TabContols of my project to be sure I never met the problem if I make changes in future, or even add another languages :
In Russian :
In English :
I finally found a solution, maybe not the best, but it is working perfectly, and didn't find any other solution.
It is not possible to do the job when initializing the window, as the contents has not been loaded yet (MyWindow.Show()), so I had to use the SizeChanged event of TabControl.
On my ViewModel I set two parameters TabControlWidth and TabControlHeight.
private double tabControlWidth = 0;
public double TabControlWidth
{
get { return tabControlWidth; }
set
{
if (this.tabControlWidth != value)
{
tabControlWidth = value;
this.NotifyPropertyChanged("TabControlWidth");
}
}
}
private double tabControlHeight = 0;
public double TabControlHeight
{
get { return tabControlHeight; }
set
{
if (this.tabControlHeight != value)
{
tabControlHeight = value;
this.NotifyPropertyChanged("TabControlHeight");
}
}
}
in my file MyWindow.xaml.cs I add a bool property to check when I checked all TabItems(obviously, this property is set to false when initializing the window) :
bool allTabItemsChecked { get; set; }
Finally, I need to add to my TabControl two things :
Bind Width and Height to my properties in ViewModel.
Add event SizeChanged
SizeChanged="TabControlSizeChanged" MinHeight="{Binding TabControlHeight}" MinWidth="{Binding TabControlWidth}"
(excuse me, I don't manage to display the last line as code? often happens after I use "-" or "*")
Finally I make the SizeChanged function as following :
private void TabControlSizeChanged(object sender, SizeChangedEventArgs e)
{
if(this.allTabItemsChecked==false)
{
int index = tabControl.SelectedIndex;
List<TabItem> list = new List<TabItem>();
list.AddRange(tabControl.Items.OfType<TabItem>());
if (index < list.Count()-1)
{
tabControl.SelectedIndex = index + 1;
}
else
{
this.allTabItemsChecked = true;
tabControl.SelectedIndex = 0;
}
contexte.TabControlWidth = Math.Max(tabControl.ActualWidth, contexte.TabControlWidth);
contexte.TabControlHeight = Math.Max(tabControl.ActualHeight, contexte.TabControlHeight);
}
}
When we display the window, event is automatically launched.
I will then change TabControl.SelectedIndex to the next TabItem, and update TabControl dimensions to the bigger one.
When I reached the last index, I just set SelectedIndex to 0, then set allTabItemsChecked to true.

Automation Peer of IScrollProvider is always null

I am trying to implement a way to move back the scroll bar of a datagrid back to a previous position following this sample http://www.codeproject.com/Articles/109531/Controlling-and-Viewing-the-ScrollBar-Positions-of but this method always returns null making it impossible to create automation. Does any have an idea why this would always return null?
public static IScrollProvider GetScrollProvider(DataGrid grid)
{
var p = FrameworkElementAutomationPeer.FromElement(grid)
?? FrameworkElementAutomationPeer.CreatePeerForElement(grid);
return p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
}
Ancient history, but at least current version seems to work liek charm in all cases.
//p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
{System.Windows.Automation.Peers.DataGridAutomationPeer}
[System.Windows.Automation.Peers.DataGridAutomationPeer]:
{System.Windows.Automation.Peers.DataGridAutomationPeer}
HorizontallyScrollable: true
HorizontalScrollPercent: 0.0
HorizontalViewSize: 81.44329896907216
VerticallyScrollable: true
VerticalScrollPercent: 29.062733871459486
VerticalViewSize: 2.9625
I know its a old post, but in case someone gets here with the same problem, (Like me), here is how i solved it:
For some reason, when you the the scrollbar of the DataGrid, if the user didn't scroll yet, it will inform that the maximum position is 0, even if it isn't. So i saved the maximum value before updating and restore it after:
First i create a class to save those values. It could be variables, but a class seemed more organized:
public class ScrollInfo
{
public double HorizontalPosition;
public double HorizontalMaximum;
public double VerticalPosition;
public double VerticalMaximum;
}
Before i update the ItemSource property i use this method to save Scroll info:
public static ScrollInfo GetScrollInfo(this DataGrid grid)
{
ScrollInfo oInfo = new ScrollInfo();
ScrollBar sbHorizontal = grid.GetScrollbar(ScrollMode.Horizontal);
oInfo.HorizontalMaximum = sbHorizontal.Maximum;
oInfo.HorizontalPosition = sbHorizontal.Value;
ScrollBar sbVertical = grid.GetScrollbar(ScrollMode.Vertical);
oInfo.VerticalMaximum = sbVertical.Maximum;
oInfo.VerticalPosition = sbVertical.Value;
return oInfo;
}
And then i call this method to set this info back to the grid after the update:
public static void SetScrollPosition(this DataGrid grid, ScrollInfo info)
{
if (info.HorizontalPosition > 0)
{
ScrollBar sbHorizontal = grid.GetScrollbar(ScrollMode.Horizontal);
sbHorizontal.Maximum = info.HorizontalMaximum;
grid.Scroll(ScrollMode.Horizontal, info.HorizontalPosition);
}
if (info.VerticalPosition > 0)
{
ScrollBar sbVertical = grid.GetScrollbar(ScrollMode.Vertical);
sbVertical.Maximum = info.VerticalMaximum;
grid.Scroll(ScrollMode.Vertical, info.VerticalPosition);
}
}
I even create a UpdateItemSource method to make things easier:
public static void UpdateItemSource(this DataGrid grid, IEnumerable itemSource)
{
ScrollInfo oInfo = grid.GetScrollInfo();
grid.ItemsSource = itemSource;
grid.SetScrollPosition(oInfo);
}
Other methods I use, wich i guess i take from the same post the user who post the question did:
public static void Scroll(this DataGrid grid, ScrollMode mode, double position)
{
// Get the scrollbar and convert the position to percent.
var scrollBar = grid.GetScrollbar(mode);
double positionPct = ((position / scrollBar.Maximum) * 100);
// Scroll to a specfic percentage of the scrollbar.
grid.ScrollToPercent(mode, positionPct);
}
public static void ScrollToPercent(this DataGrid grid, ScrollMode mode, double percent)
{
// Fix the percentage.
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
// Get the scroll provider.
var scrollProvider = GetScrollProvider(grid);
// Scroll.
switch (mode)
{
case ScrollMode.Vertical:
scrollProvider.SetScrollPercent( System.Windows.Automation.ScrollPatternIdentifiers.NoScroll, percent);
break;
case ScrollMode.Horizontal:
scrollProvider.SetScrollPercent(percent, System.Windows.Automation.ScrollPatternIdentifiers.NoScroll);
break;
}
}

Categories