How to refresh WPF chart after updating the Y Axis Maximum value? - c#

I 'm creating a Line chart using WPFToolKit.
The chart comes up fine but i want to change the Maximum property of the Y Axis upon button click of a modal window after loading the chart for the first time. And the chart should be refreshed with the updated Y Axis Max value
Below line shows how Chart is defined in xaml.
<DVC:Chart Canvas.Top="80" Canvas.Left="10" Name="mcChart" VerticalAlignment="Stretch"/>
I'm calling below code in windows.xaml.cs constructor and it is setting Y-Axis Max to 200
mcChart.Axes.Add(new LinearAxis()
{
Minimum = 0,
Maximum = YMax > 0 ? YMax : 200,
Orientation = AxisOrientation.Y,
ShowGridLines = true,
});
mcChart.UpdateLayout();
How can i change the Y-Axis Max value from a modal window's button click event and refresh chart to display with new YMax.
I'm not sure if i have to do something with RegisteredListeners.
I'm new to WPF and any help is appreciated!
Please note that i'm looking to achieve this from the C# code behind and not in xaml.
Thanks,
Sujay

If you have access to the chart, you can find the neccessary axis and change the Maximum property without updating layout. Here is an example with a linear Y axis:
var yAxis = this.mcChart.ActualAxes.OfType<LinearAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.Y);
if (yAxis != null)
yAxis.Maximum = 300;
The complete version of this example:
MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Set max value = 300" HorizontalAlignment="Center" Click="Button_Click"/>
<charting:Chart Grid.Row="1" x:Name="mcChart">
<charting:Chart.Series>
<charting:LineSeries ItemsSource="{Binding LineItems}" IndependentValuePath="Date" DependentValuePath="Value"/>
</charting:Chart.Series>
</charting:Chart>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//Add a linear Y axis
int YMax = 150;
mcChart.Axes.Add(new LinearAxis()
{
Minimum = 0,
Maximum = YMax > 0 ? YMax : 200,
Orientation = AxisOrientation.Y,
ShowGridLines = true,
});
//Create and set a view model
var items = Enumerable.Range(0, 50).Select(i => new ChartItemModel { Date = new DateTime(2010, 1, 1).AddDays(i), Value = 30 + i }).ToList();
this.DataContext = new MainViewModel { LineItems = items };
}
//Set Maximum=300
private void Button_Click(object sender, RoutedEventArgs e)
{
var yAxis = this.mcChart.ActualAxes.OfType<LinearAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.Y);
if (yAxis != null)
yAxis.Maximum = 300;
}
}
public class MainViewModel
{
public List<ChartItemModel> LineItems { get; set; }
}
public class ChartItemModel
{
public DateTime Date { get; set; }
public double Value { get; set; }
}

Related

Error This PlotModel is already in use by some other PlotView control in OxyPlot chart

I am using Xamarin.Forms OxyPlot Chart. I have a collectionview and in each collectionview item has an expander and inside each of those expanders is a PlotView
<CollectionView x:Name="Kids">
<CollectionView.ItemTemplate>
<DataTemplate>
<xct:Expander Tapped="Expander_Tapped" ClassId="{Binding rowNumber}">
<xct:Expander.Header>
<Frame Padding="0" CornerRadius="10" Margin="5" BackgroundColor="White" HasShadow="False">
<StackLayout>
<Grid BackgroundColor="#f8f8f8">
<StackLayout Padding="5" Orientation="Horizontal">
<Image x:Name="kidProfile" Source="{Binding image}" WidthRequest="75" HeightRequest="75" HorizontalOptions="Start" Aspect="AspectFill" />
<StackLayout Orientation="Vertical">
<Label Text="{Binding first_name}"></Label>
<StackLayout Orientation="Horizontal">
<Label Text="Grade: " FontSize="Small"></Label>
<Label Text="{Binding grade}" FontSize="Small"></Label>
</StackLayout>
</StackLayout>
</StackLayout>
<Image Margin="20" HorizontalOptions="End" Source="arrowDown.png" HeightRequest="15"></Image>
</Grid>
</StackLayout>
</Frame>
</xct:Expander.Header>
<oxy:PlotView Model="{Binding chart}" HeightRequest="200" WidthRequest="100" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
and I was assigning the PlotModel in my class
public class ReportsClass
{
public PlotModel chart
{
get
{
PlotModel model = new PlotModel();
CategoryAxis xaxis = new CategoryAxis();
xaxis.Position = AxisPosition.Bottom;
xaxis.MajorGridlineStyle = LineStyle.None;
xaxis.MinorGridlineStyle = LineStyle.None;
xaxis.MinorTickSize = 0;
xaxis.MajorTickSize = 0;
xaxis.TextColor = OxyColors.Gray;
xaxis.FontSize = 10.0;
xaxis.Labels.Add("S");
xaxis.Labels.Add("M");
xaxis.Labels.Add("T");
xaxis.Labels.Add("W");
xaxis.Labels.Add("T");
xaxis.Labels.Add("F");
xaxis.Labels.Add("S");
xaxis.GapWidth = 10.0;
xaxis.IsPanEnabled = false;
xaxis.IsZoomEnabled = false;
LinearAxis yaxis = new LinearAxis();
yaxis.Position = AxisPosition.Left;
yaxis.MajorGridlineStyle = LineStyle.None;
xaxis.MinorGridlineStyle = LineStyle.None;
yaxis.MinorTickSize = 0;
yaxis.MajorTickSize = 0;
yaxis.TextColor = OxyColors.Gray;
yaxis.FontSize = 10.0;
yaxis.FontWeight = FontWeights.Bold;
yaxis.IsZoomEnabled = false;
yaxis.IsPanEnabled = false;
ColumnSeries s2 = new ColumnSeries();
s2.TextColor = OxyColors.White;
s2.Items.Add(new ColumnItem
{
Value = Sunday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Monday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Tuesday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Wednesday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Thursday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Friday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = Saturday,
Color = OxyColor.Parse("#02cc9d")
});
model.Axes.Add(xaxis);
model.Axes.Add(yaxis);
model.Series.Add(s2);
model.PlotAreaBorderColor = OxyColors.Transparent;
return model;
}
}
}
Now this works, but in the Expander when I expand an item the PlotView would not show, at all. So I changed my Class to use INotifyPropertyChanged
public class ReportsClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PlotModel chart
{
get => _chart;
set
{
_chart = value;
if(_chart.PlotView == null && value.PlotView == null)
{
OnPropertyChanged("chart");
}
}
}
public PlotModel _chart;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
if(this.chart.PlotView == null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And in my code behind I use the expander's tapped method to populate the PlotView:
void Expander_Tapped(System.Object sender, System.EventArgs e)
{
if(expander != null)
{
expander.IsExpanded = false;
}
expander = sender as Expander;
int id = Convert.ToInt32(expander.ClassId);
ReportsClass item = newKidList[id];
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
if (item.chart == null)
{
PlotModel model = new PlotModel();
CategoryAxis xaxis = new CategoryAxis();
xaxis.Position = AxisPosition.Bottom;
xaxis.MajorGridlineStyle = LineStyle.None;
xaxis.MinorGridlineStyle = LineStyle.None;
xaxis.MinorTickSize = 0;
xaxis.MajorTickSize = 0;
xaxis.TextColor = OxyColors.Gray;
xaxis.FontSize = 10.0;
xaxis.Labels.Add("S");
xaxis.Labels.Add("M");
xaxis.Labels.Add("T");
xaxis.Labels.Add("W");
xaxis.Labels.Add("T");
xaxis.Labels.Add("F");
xaxis.Labels.Add("S");
xaxis.GapWidth = 10.0;
xaxis.IsPanEnabled = false;
xaxis.IsZoomEnabled = false;
LinearAxis yaxis = new LinearAxis();
yaxis.Position = AxisPosition.Left;
yaxis.MajorGridlineStyle = LineStyle.None;
xaxis.MinorGridlineStyle = LineStyle.None;
yaxis.MinorTickSize = 0;
yaxis.MajorTickSize = 0;
yaxis.TextColor = OxyColors.Gray;
yaxis.FontSize = 10.0;
yaxis.FontWeight = FontWeights.Bold;
yaxis.IsZoomEnabled = false;
yaxis.IsPanEnabled = false;
ColumnSeries s2 = new ColumnSeries();
s2.TextColor = OxyColors.White;
s2.Items.Add(new ColumnItem
{
Value = item.Sunday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Monday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Tuesday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Wednesday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Thursday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Friday,
Color = OxyColor.Parse("#02cc9d")
});
s2.Items.Add(new ColumnItem
{
Value = item.Saturday,
Color = OxyColor.Parse("#02cc9d")
});
model.Axes.Add(xaxis);
model.Axes.Add(yaxis);
model.Series.Add(s2);
model.PlotAreaBorderColor = OxyColors.Transparent;
item.chart = model;
}
return false;
});
}
However, eventually I will get this error:
This PlotModel is already in use by some other PlotView control
Now I understand that there is a one-to-one relation between the PlotView and its PlotModel, which gives us the error, so I have tried to do a check to see if PlotModel has a PlotView, but I am still getting this error.
I found this solution:
If you set the parent View of your OxyPlot to a DataTemplate that
creates a new OxyPlot each time then the OxyPlot cannot be cached. A
new PlotModel and PlotView is created each time and this error is
avoided (at least that seems to work for me, I am using CarouselView)
https://github.com/oxyplot/oxyplot-xamarin/issues/17
But I do not know how to do this for a collection view, any help would be much apperciated.
I also found this:
This is a very common issue of OxyPlot when it's used in MVVM. In
OxyPlot, the view and the model are 1-to-1 mapped, when a second view
is trying to bind the same PlotModel, you have the issue of "PlotModel
is already in use by some other PlotView control". On iOS, ListView's
cell will be re-created when it is scrolling. In this case, there will
be a newly created view trying to bind the same PlotModel, and then
you have the issue. On Android, I guess you will have the same issue
too. Try to put your phone from landscape to portrait, see if you have
the same issue. In Android, the view will be re-created completely
when the orientation is changed.
A quick fix is to break the MVVM design here a little bit. Don't
create the model in a separated place but create the model in the
view. So whenever a view is re-created by iOS or Android, a new model
is also re-created.
https://github.com/oxyplot/oxyplot-xamarin/issues/60
But I don't know how to apply this part:
A quick fix is to break the MVVM design here a little bit. Don't
create the model in a separated place but create the model in the
view. So whenever a view is re-created by iOS or Android, a new model
is also re-created.
See github ToolmakerSteve / repo OxyplotApp1, for working version.
"This PlotModel is already in use by some other PlotView control"
After various tests on iOS, I conclude that using (CollectionView or ListView) + Expander + Oxyplot on iOS is fundamentally not reliable.
Oxyplot seems to worsen known issues with Expander and CollectionView.
Therefore, the most important fix is to stop using these collection views. Replace use of CollectionView with:
<StackLayout Spacing="0" BindableLayout.ItemsSource="{Binding KidModels}">
<BindableLayout.ItemTemplate>
<DataTemplate>
For better performance, make this change, so each graph is only created the first time a user clicks a given item:
void Expander_Tapped(System.Object sender, System.EventArgs e)
{
// --- ADD THESE LINES ---
if (ReferenceEquals(sender, expander)) {
// User is tapping the existing expander. Don't do anything special.
return;
}
...
}
NOTE: Also fixes a problem where expander immediately closed again, if user tapped it three times in a row.
Faster appearance the first time each expander is clicked. Here are three alternatives. From fastest to slowest. Use the first, but if ever get a blank graph or other problem, switch to second. If second still has problems, switch to third - which is the original, though with a slightly shorter time delay:
if (item.Chart == null) {
PlotModel model = CreateReportChart(item);
Action action = () => {
item.Chart = model;
};
if (false) {
action();
} else if (true) {
Device.BeginInvokeOnMainThread(() => {
action();
});
} else {
Device.StartTimer(TimeSpan.FromSeconds(0.5), () => {
action();
return false;
});
}
}
OPTIONAL: To be sure expander animation doesn't cause a problem when used with Oxyplot.
If having problems that occur, but only "sometimes", try this, see if situation improves:
<xct:Expander AnimationLength="0" ...>
That should remove the animation.
You can remove tests such as if (_chart.PlotView == null && value.PlotView == null) from your OnPropertyChanged-related code. That is, ALWAYS do the OnPropertyChanged.
REASON: in the future you might wish to generate a modified plot because data changed, and the tests you have would prevent UI from seeing the change.

Do you have any idea why returned List items from Class not working?

First of all English is not my native language and title is probably horrible so I appologize, and moderators feel free to edit title if you want.
I have method createGrpButtons in class Receipt, which instantiate class CreateGrpButtons, and use returned List to create buttons on form. If I use same code inside method it works, but when i move code into the class it stop working. List items are still passed(i can see them while debbuging), but buttons are not created.
public class TodoItem
{
public string Content { get; set; }
public string Margin { get; set; }
public string Tag { get; set; }
public int Height { get; set; }
public int Width { get; set; }
}
private void createGrpButtons()
{
int stPanelHeight = (Convert.ToInt16(stPanel.ActualHeight));
int stPanelWidth = (Convert.ToInt16(stPanel.ActualWidth));
GenerateGrpButtons btnGenGrp = new GenerateGrpButtons();
btnList.ItemsSource = btnGenGrp.CreateGrpButtons(70, 0, stPanelHeight, stPanelWidth);
}
And here is createGrpButton Class
class GenerateGrpButtons:frmReceipt
{
public List<TodoItem> CreateGrpButtons( int btnMinimumHeightSize, int separationY, int pnlHeight, int pnlWidth)
{
//Calculate size of container to determine numbers of button
//int btnMinimumHeightSize = 40;
//int separationY = 0; //separation between buttons
int btnNumberCreated = (pnlHeight / btnMinimumHeightSize);
List<TodoItem> btns = new List<TodoItem>();
for (int i = 0; i < btnNumberCreated; i++)
{
if (i == btnNumberCreated - 1)
{
var HeightTmp = (Convert.ToDouble(stPanel.ActualHeight)) - (btnMinimumHeightSize * i);
btns.Add(new TodoItem() { Content = "ˇˇˇˇ", Height = Convert.ToInt16(HeightTmp), Width = Convert.ToInt16(stPanel.ActualWidth), Tag = "LastGrp", Margin = "0,0,0,0" });
}
else
{
btns.Add(new TodoItem() { Content = "Group " + i, Height = btnMinimumHeightSize, Width = Convert.ToInt16(stPanel.ActualWidth), Tag = "Grp" + Convert.ToString(i), Margin = "1," + separationY + ",0,0" });
}
}
return btns;
}
}
When I debug here:
btnList.ItemsSource = btnGenGrp.CreateGrpButtons(70, 0, stPanelHeight, stPanelWidth);
I can see that there are some number of items created, and i can see item properties, but buttons are not created on form.
However if i do this (everything in method), then buttons appear on the form.
//Calculate size of container to determine numbers of button
int btnMinimumHeightSize = 40;
int separationY = 0; //separation between buttons
int btnNumberCreated = ((Convert.ToInt16(stPanel.ActualHeight) / btnMinimumHeightSize));
List<TodoItem> btns = new List<TodoItem>();
for (int i = 0; i < btnNumberCreated; i++)
{
if (i == btnNumberCreated - 1)
{
var HeightTmp = (Convert.ToDouble(stPanel.ActualHeight)) - (btnMinimumHeightSize * i);
btns.Add(new TodoItem() { Content = "ˇˇˇˇ", Height = Convert.ToInt16(HeightTmp), Width = Convert.ToInt16(stPanel.ActualWidth), Tag = "LastGrp", Margin = "0,0,0,0" });
}
else
{
btns.Add(new TodoItem() { Content = "Group " + i, Height = btnMinimumHeightSize, Width = Convert.ToInt16(stPanel.ActualWidth), Tag = "Grp" + Convert.ToString(i), Margin = "1," + separationY + ",0,0" });
}
}
btnList.ItemsSource = btns;
Buttons are binded through this:
<StackPanel Grid.Column="1" Grid.Row="1" Name="stPanel" HorizontalAlignment="Stretch" >
<ItemsControl Name="btnList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content ="{Binding Content}" Height="{Binding Height}" Width="{Binding Width}" Tag="{Binding Tag}" Margin="{Binding Margin}" HorizontalAlignment="Center" Click="ClickHandlerGrp" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Anybody have a clue on where did I messed up?
Even though the stPanelHeight and stPanelWidth values are correctly supplied to the method, the method still accesses stPanel.ActualHeight and stPanel.ActualWidth directly.

c# wpf - ListView.ScrollIntoView(LastItem) does not work properly

like some other people here i have a ListView (updated via binding in a GridView).
I want to keep the last inserted Item in the View. So i tried
LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
This is working almost fine. Altough the first item which would have to be scrolled into view is only shown like 80% of its whole row (depending on how high i define the whole ListView, i almost got 100%).
The real problem is that the following items which should get scrolled into view are not shown. It is also noticable at the Scrollbar itself which is not at the bottom.
Last Item is not shown
Here is the code of my MainWindow.
public partial class MainWindow : Window
{
private InterfaceCtrl ICtrl;
private ListView LView;
public MainWindow()
{
InitializeComponent();
this.ICtrl = new InterfaceCtrl();
this.ICtrl.ProgressCollection.CollectionChanged += this.CollectionChanged;
Grid MainGrid = new Grid();
this.Content = MainGrid;
GridView gv = new GridView();
Binding StartTimeStampBinding = new Binding() { Path = new PropertyPath("StartTS"), Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" };
GridViewColumn gvTCStartTS = new GridViewColumn() { Header = "Time", Width = 150.00, DisplayMemberBinding = StartTimeStampBinding };
gv.Columns.Add(gvTCStartTS);
LView = new ListView() { Height = 192, Width = 250, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, View = gv, ItemsSource = this.ICtrl.ProgressCollection };
MainGrid.Children.Add(LView);
ICtrl.StartMyThread();
}
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
//LView.ScrollIntoView(LView.Items[LView.Items.Count - 1]);
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
}
Thank you.
EDIT:
It seemed to be a timing problem, although all the wanted data was in the LView at the right time i tried a workaround with a Textbox bound to the Timestamp.
TextBox tb = new TextBox(); // { Width = 250, Height = 28, Margin= new Thickness(10,100,1,0)};
tb.SetBinding( TextBox.TextProperty , new Binding("LastMsgTimestamp") { Source = this.ICtrl, Mode = BindingMode.OneWay, StringFormat = "dd.MM.yyyy - HH:mm:ss.fff" });
tb.TextChanged += this.UpdateScrollbar;
tb.Visibility = Visibility.Hidden;
It seems to me like there is a timing issue within the binding to the LView and the fired Event of the ObservableCollection. This also includes the PropertyChanged of the ObservableCollection.
I tried the events TargetUpdated and SoruceUpdated directly within LView but those didn't came up at all.
You could try to call any of the ScrollToBottom() or ScrollToVerticalOffset() methods of the ListView's internal ScrollViewer element:
private void CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new System.Action(delegate ()
{
if (LView != null && LView.Items.Count > 0)
{
LView.UpdateLayout();
ScrollViewer sv = GetChildOfType<ScrollViewer>(LView);
if (sv != null)
sv.ScrollToBottom();
LView.SelectedIndex = LView.Items.Count;
LView.ScrollIntoView(LView.SelectedItem);
}
}));
}
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null)
return result;
}
return null;
}
I have made the following sample. You could try to call ScrollToBottom in inner ScrollViewer as #mm8 points out. Nevertheless when saw the answer I was already making my sample, so here it is:
Codebehind
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ListViewScroll
{
public partial class MainWindow : Window
{
public ObservableCollection<string> Names { get; set; }
public MainWindow()
{
InitializeComponent();
Names = new ObservableCollection<string>();
ListView.ItemsSource = Names;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Names.Add("Some Name" + ++someInt);
// Get the border of the listview (first child of a listview)
var border = VisualTreeHelper.GetChild(ListView, 0) as Decorator;
// Get scrollviewer
var scrollViewer = border.Child as ScrollViewer;
scrollViewer.ScrollToBottom();
}
private static int someInt;
}
}
XAML
<Window x:Class="ListViewScroll.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" Name="ListView"/>
<Button Content="Add" FontSize="20" Grid.Row="1"
Click="ButtonBase_OnClick"/>
</Grid>
</Window>
In this case I am handling the scrolling in the button click event but you may change this to fit your requirements
It works, I have tested.
Hope this helps

Navigation Cocossharp and xamarin.forms

I have 2 pages, In first page when I click one of the item in listview, it should navigate to 2nd page and here in 2nd page snow particles are appeared.
After that, when I click back button, as usual it goes to first page and again when I click any of the listview items it navigates to 2nd page snow particles disappear,It just show a black screen.
Here are the codes:
For snow rendering I have two classes
First one
public class SnowScene : CCScene
{
CCParticleSnow snow;
public SnowScene(CCGameView gameView) : base(gameView)
{
var layer = new CCLayer();
layer.Color=new CCColor3B(new CCColor4B(20,30,50,10));
snow = new CCParticleSnow(new CCPoint(490, 20));
snow.Position = new CCPoint(490 / 2, 800 + 1);
snow.StartColor = new CCColor4F(CCColor4B.White);
snow.EndColor = new CCColor4F(CCColor4B.Red);
snow.StartSize = 15f;
snow.StartSizeVar = 2f;
snow.EndSize = 8f;
snow.EndSizeVar = 1f;
snow.Speed = 5f;
snow.Gravity = new CCPoint(0.5f, -2);
snow.EmissionRate = 3.5f;
snow.Life = 50f;
layer.AddChild(snow);
this.AddLayer(layer);
}
}
Second one
public class SnowyBackground : ContentView
{
SnowScene overallScene;
public SnowyBackground()
{
var sharpView = new CocosSharpView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
sharpView.ViewCreated += (sender, e) =>
{
var ccGView = sender as CCGameView;
if (ccGView != null)
{
ccGView.DesignResolution = new CCSizeI(490, 800);
overallScene = new SnowScene(ccGView);
ccGView.RunWithScene(overallScene);
}
};
Content = sharpView;
}
}
And in Xaml in Second page
Now When I click an Item of listview in first page(page1) like below:
Event handler for Listview item click in first page-
async void Handle_ItemSelected(object sender, Xamarin.Forms.SelectedItemChangedEventArgs e)
{
try
{
await Navigation.PushModalAsync(new Page2(),false);
}
catch(Exception ex)
{ }
}
2nd page -
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Particle"
x:Class="Particle.ParticlePage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:SnowyBackgroundView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Grid.RowSpan="2" />
</Grid>
It navigates to page 2 and it successfully shows snow rendering. But when I repeat same steps... I mean If I click back button in page 2,then it goes to page1 and here when again I click lictview item,it goes to second page, but it doesnot show snow rendering, just a black screen.

Create StackPanels dynamically and refer to any of them in code behind

In its simplest form...
I would like to create as many StackPanels as I want and then add Rectangles in them. Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.
Any help would be appreciated.
For example, if our favorite beer wrote the framework I could do it like this:
XAML:
<Page
x:Class="Test2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Name="StartButton" Content="Start" Click="StartButton_Click" Height="30" Width="200" Margin="5"/>
</StackPanel>
<StackPanel Grid.Row="1" Name="myStackPanel" VerticalAlignment="Top"/>
</Grid>
</Page>
Code Behind:
namespace Test2
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
for (var i = 0; i < 5; i++) // The 5 here could be any number
{
myStackPanel.Children.Add(new StackPanel
{
Name = "myPanel" + i,
Orientation = Orientation.Horizontal
});
for (var j = 0; j < 10; j++) // The 10 here could be any number
{
("myPanel" + i).Children.Add(new Rectangle
{
Name = "myRectangle" + i + "-" + j,
Fill = new SolidColorBrush(Colors.Black),
Width = 20,
Height = 20,
Margin = new Thickness(1)
});
}
}
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
// E.G. To change the Fill color of Rectangle4 in StackPanel2
("myRectangle" + 2 + "-" + 4).Fill = new SolidColorBrush(Colors.Red);
}
}
}
Firstly, to add Rectangle shapes, we can create an instance of StackPanel and manipulate its Children elements:
for (var i = 0; i < 5; i++) // The 5 here could be any number
{
StackPanel sp = new StackPanel
{
Name = "myPanel" + i,
Orientation = Orientation.Horizontal
};
myStackPanel.Children.Add(sp);
for (var j = 0; j < 10; j++) // The 10 here could be any number
{
sp.Children.Add(new Rectangle
{
Name = "myRectangle" + i + "-" + j,
Fill = new SolidColorBrush(Colors.Black),
Width = 20,
Height = 20,
Margin = new Thickness(1)
});
}
}
Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.
As tgpdyk mentioned, we need to use VisualTreeHelper to find the specified rectangle shape.
Helper class:
public static class FrameworkElementExtensions
{
public static T TraverseCTFindShape<T>(DependencyObject root, String name) where T : Windows.UI.Xaml.Shapes.Shape
{
T control = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
{
var child = VisualTreeHelper.GetChild(root, i);
string childName = child.GetValue(FrameworkElement.NameProperty) as string;
control = child as T;
if (childName == name)
{
return control;
}
else
{
control = TraverseCTFindShape<T>(child, name);
if (control != null)
{
return control;
}
}
}
return control;
}
}
How to use it:
private void StartButton_Click(object sender, RoutedEventArgs e)
{
// E.G. To change the Fill color of Rectangle4 in StackPanel2
var rec = FrameworkElementExtensions.TraverseCTFindShape<Shape>(myStackPanel, "myRectangle" + 2 + "-" + 4);
rec.Fill = new SolidColorBrush(Colors.Red);
}
I've uploaded my sample to Github repository
That is not how you approach this in WPF, at all.
You usually do not concern yourself with any UI components but only the data. In this case you data bind an ItemsControl to a list of rows, each row containing a list of cells. In the ItemsControl definition you then set an ItemTemplate that contains another ItemsControl binding to the cells. In the nested ItemsControl you then can set an ItemTemplate where you bind the Background to a (notifying) property of your cells which you then just need to change in code.
Check out these overviews:
Data Binding Overview
Data Templating Overview
You may also want to look into the Model-View-ViewModel pattern to ensure a suitable application architecture.

Categories