Add Shape Information to a Listview When Its Created

Add Shape information to a ListView when its created

In a proper MVVM approach, you should have a view model with an abstract representation of a Shape (instead of a list of UI elements), e.g. like this:

public class ShapeData
{
public string Type { get; set; }
public Geometry Geometry { get; set; }
public Brush Fill { get; set; }
public Brush Stroke { get; set; }
public double StrokeThickness { get; set; }
}

public class ViewModel
{
public ObservableCollection<ShapeData> Shapes { get; }
= new ObservableCollection<ShapeData>();
}

You could now bind this view model to a view like shown below. The position and size of each shape is retrieved from the Bounds property of the Geometry of a shape object.

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>

<ItemsControl ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path Data="{Binding Geometry}"
Fill="{Binding Fill}"
Stroke="{Binding Stroke}"
StrokeThickness="{Binding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

<ListView Grid.Column="1" ItemsSource="{Binding Shapes}">
<ListView.View>
<GridView>
<GridViewColumn Header="Type"
DisplayMemberBinding="{Binding Type}"/>
<GridViewColumn Header="X"
DisplayMemberBinding="{Binding Geometry.Bounds.X}"/>
<GridViewColumn Header="Y"
DisplayMemberBinding="{Binding Geometry.Bounds.Y}"/>
<GridViewColumn Header="Width"
DisplayMemberBinding="{Binding Geometry.Bounds.Width}"/>
<GridViewColumn Header="Height"
DisplayMemberBinding="{Binding Geometry.Bounds.Height}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>

You could create a view model instance in your window's constructor and add some sample data like this:

public MainWindow()
{
InitializeComponent();

var viewModel = new ViewModel();

viewModel.Shapes.Add(new ShapeData
{
Type = "Circle",
Geometry = new EllipseGeometry(new Point(100, 100), 50, 50),
Fill = Brushes.Orange,
Stroke = Brushes.Navy,
StrokeThickness = 2
});

viewModel.Shapes.Add(new ShapeData
{
Type = "Rectangle",
Geometry = new RectangleGeometry(new Rect(200, 50, 50, 100)),
Fill = Brushes.Yellow,
Stroke = Brushes.DarkGreen,
StrokeThickness = 2
});

DataContext = viewModel;
}

How do you draw a shape on a ListView in Android

I did the same kind of view. I developed my own CustomView and kept it in a ScrollView. This customView's height is 24 * 60 dip. So the height of the view will be increased as we kept dip instead of px (I hope you know difference between the px and dip) and the width will be the width of the device screen.

I can't share you the complete code. But, can partially paste here. This will bring you a clear picture handling every thing.

public class CustomDayView extends View implements OnTouchListener{

private Paint p;
private Paint textp;
private Paint roundRectP;
private int parentWidth = 0;
private int parentHeight = 0;
private int X_OFFSET = 5;
private int Y_OFFSET = 5;
private int HOUR_BLOCK_HEIGHT = 60;
private int font_max_width;
private ScrollView _scrollView;
private int least_time_in_hours = 24*60;//6 * 60
private RectF _rects[];
private int font_height;
private Context context;

public CustomDayEmployeeView(Context context) {
super(context);
this.context = context;
init();
}
public CustomDayEmployeeView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public CustomDayEmployeeView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init();
}
private void init(){
//Creating Paint Objects
//setting color
//setting Fonts
this.setOnTouchListener(this);
calculateRectValues();
}

private void calculateRectValues() {
// Calculating the Rects to draw in the paint this includes x,y points starting of the rect and width and height of the rectangle.
int font_max_width = calculate the width needed to draw the hours from 0 to 24 hours;
for(int i=0;i<no of appts;i++)
_rects[j] = new RectF(font_max_width, convertTimetoSeconds("09:30"), screenwidth-font_max_width , convertTimetoSeconds("11:30");

}

private int convertTimetoSeconds(String _startTime) {
// TODO Auto-generated method stub
int total = Integer.parseInt(_startTime.substring(0,_startTime.indexOf(":"))) * 60;
total += Integer.parseInt(_startTime.substring(_startTime.indexOf(":")+1, _startTime.length()));
return total;
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
parentWidth = MeasureSpec.getSize(widthMeasureSpec);
parentHeight = MeasureSpec.getSize(heightMeasureSpec);
calculateRectValues();
setMeasuredDimension(screenwidth,24 * HOUR_BLOCK_HEIGHT);
}

public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);

for(int i=0;i<25;i++)
{
String preString = "";
String poststring = "";

if(i == 12)
{
preString = "Noon";
poststring = "";

}
else if(i%12 == 0)
{
preString = "12";
poststring = " AM";
}
else if(i<12)
{
preString = i+"";
poststring = " AM";
}
else
{
preString = i%12+"";
poststring = " PM";
}
canvas.drawText(preString, X_OFFSET+3, i * HOUR_BLOCK_HEIGHT + font_height, p);
canvas.drawText(poststring, X_OFFSET+p.measureText(preString), i * HOUR_BLOCK_HEIGHT + font_height, p);
p.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
p.setColor(Color.parseColor("#cbcaca"));
p.setStrokeWidth(0.2f);
p.setPathEffect(new DashPathEffect(new float[] {1,2}, 0));
canvas.drawLine(font_max_width, i * (HOUR_BLOCK_HEIGHT)+ font_height/2+HOUR_BLOCK_HEIGHT/2, parentWidth-8, i * (HOUR_BLOCK_HEIGHT)+ font_height/2+HOUR_BLOCK_HEIGHT/2, p);
p.setColor(Color.parseColor("#f1f1f1"));
p.setPathEffect(new PathEffect());
p.setStrokeWidth(0.2f);
canvas.drawLine(font_max_width, i * HOUR_BLOCK_HEIGHT+ font_height/2, parentWidth-8, i * HOUR_BLOCK_HEIGHT+ font_height/2, p);

}
for(int j=0;j<no of appts;j++)
{
canvas.drawRoundRect(_rects[j], 3, 3, roundRectP);
canvas.drawText(obj._title, _rects[j].left+X_OFFSET,_rects[j].top + font_height + Y_OFFSET, textp);
}
}
}

public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
int x = (int) event.getX();
int y = (int) event.getY();
for(int j=0;j<no of appts;j++)
{
if(_rects[j].contains(x, y))
{
//Selected J appointmrnt
}
}
return true;
}
}

I think this will help you. Just create Object and pass you appointments in an ArrayList to init of this object and do the tweaks that you need to it.

Display shapes in WPF ListView dynamically

Okay, I finally accomplished what I wanted to see. First let's look into XAML. Here is only the part with the ListView and only the single column that contains the segments. Of course you need a window around this and can add more columns.

    <ListView Name="KnownImages" ItemsSource="{Binding FileViewModels}">
<ListView.View>
<GridView>

<GridViewColumn Header="Segments" Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate DataType="local:FileViewModel">
<ItemsControl Width="auto" Height="auto" ItemsSource="{Binding Segments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:SegmentViewModel">
<Line ToolTip="{Binding ToolTip}" X1="1" X2="1" Y1="{Binding YTop}" Y2="15" StrokeThickness="3" Stroke="{Binding Stroke}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

</GridView>
</ListView.View>
</ListView>

The main window has a view model that contains

    public ObservableCollection<FileViewModel> FileViewModels { get; set; }

The ListView has a binding to that field.
The DataTemplate has a view model FileViewModel that contains stuff for a single line in the list view that visualises one file. It mostly contains

public ObservableCollection<SegmentViewModel> Segments { get; set; }

So we can bind the ItemsControl to that field Segments. The WrapPanel inside the ItemsPanelTemplate is now populated with a DataTemplate that knows its data type to be SegmentViewModel:

/// <summary>
/// View model of a single file segment, which is represented with
/// a vertical line. The changeable parts of the line are stored here
/// and are directly derived from the state of the segment in the
/// constructor (ToolTip, Color and the upper Y coordinate of the line)
/// </summary>
public class SegmentViewModel
{
private const int YTopEof=0;
private const int YTopIntermediate=3;

public string ToolTip { get; set; }
public Brush Stroke { get; set; }
public int YTop { get; set; }

/// <summary>
/// Derive appearance of the line from the business data received from queue
/// if we already received data for the segment.
/// </summary>
/// <param name="fromQ">our knowledge about the segment state</param>
public SegmentViewModel(SegmentState fromQ)
{
var sb = new StringBuilder();
sb.Append("Seg ");
sb.Append(fromQ.Number);
if (fromQ.IsInMemory)
sb.Append(", in memory");
if (fromQ.IsOnDisk)
sb.Append(", on disk");
if (!fromQ.IsComplete)
sb.Append(", INCOMPLETE!!!");

ToolTip = sb.ToString();

Color col;
if (!fromQ.IsComplete)
col = Colors.Magenta;
else if (fromQ.IsInMemory)
col = fromQ.IsOnDisk ? Colors.Black : Colors.Green;
else
col = fromQ.IsOnDisk ? Colors.RoyalBlue : Colors.Brown;

Stroke = new SolidColorBrush(col);

YTop = fromQ.EndOfFile ? YTopEof : YTopIntermediate;
}

/// <summary>
/// This constructor is for segments that we did not receive so far.
/// </summary>
/// <param name="segmentNumber">We only know that we expect data from this index.</param>
public SegmentViewModel(int segmentNumber)
{
ToolTip = $"Seg {segmentNumber} not yet seen";
YTop = YTopIntermediate;
Stroke = new SolidColorBrush(Colors.LightGray);
}

For each instance of SegmentViewModel in the Segments array we get a new instance of Line as visualization. The variable visual properties are mapped with bindings (tooltip, color, line length). The non variable visual properties are constants in XAML. Note: the coordinates are not pixels but something smaller - at least on my screen.

WPF: Drawing shapes in ListView

I would use a Rectangle to draw the line and bind the Height to the thickness value and the Fill to the color value. Use the CellTemplate and set it to one of the following in the GridViewColumn.

<DataTemplate x:Key="ThicknessTemplate">
<Rectangle Height="{Binding LineThickness}" Width="10" Fill="{Binding LineColor}"/>
<DataTemplate/>
<DataTemplate x:Key="ColorTemplate">
<Rectangle Height="10" Width="10" Fill="{Binding LineColor}"/>
<DataTemplate/>

Applying a custom shape to android listview

You gave background to the listView, And list view height is 163dp, so if there are only a few items it will not fill the complete layout. so you are able to see the backgroud. the solution is instead of giving fixed height you can give wrap_content or instead of giving background to list, you should give to list item.



Related Topics



Leave a reply



Submit