Call ZoomBy method of SciChart control from ViewModel Class - c#

I am trying to call the ZoomBy() method of SciChart control from ViewModel. The ZoomBy() is easily available in the xaml.cs file like below:
// TODO: Need to implement zoom using MVVM
private void BtnZoomIn_Click(object sender, RoutedEventArgs e)
{
TemperatureGraph.ChartModifier.XAxis.ZoomBy(-0.1, -0.1);
}
The same functionality I need to implement using the ViewModel pattern.
However the ZoomExtents method is easily being called using ViewportManager of SciChart control. E.g. below: XAML file
<RocheButton Name="BtnZoomOut" DockPanel.Dock="Top" Icon="{IconResource Icon=ZoomOut}" HorizontalAlignment="Right" Command="{Binding ZoomOutCommand}" />
<s:SciChartSurface x:Name="TemperatureGraph" Grid.Column="0" s:ThemeManager.Theme="BrightSpark"
RenderableSeries="{s:SeriesBinding TemperatureGraphViewModel}" DockPanel.Dock="Bottom"
ViewportManager="{Binding ViewportManager}">
And the ViewModel Code:
public class TemperatureSummaryGraphViewModel : ViewModelBase
{
#region Private Members
private IXyDataSeries<TimeSpan, double> TemperatureDataSeries = new XyDataSeries<TimeSpan, double>();
private IXyDataSeries<TimeSpan, double> AcquisitionPointDataSeries = new XyDataSeries<TimeSpan, double>();
private DefaultViewportManager _viewportManager = new DefaultViewportManager();
private ICommand _zoomOutCommand;
#endregion
#region Constructor
public TemperatureSummaryGraphViewModel()
{
ZoomOutCommand = new DelegateCommand(() => ZoomOutTemperatureGrpah());
GenerateDummySeries();
TemperatureGraphViewModel.Add(new LineRenderableSeriesViewModel()
{
DataSeries = TemperatureDataSeries,
StyleKey = "LineSeriesStyle0"
});
TemperatureGraphViewModel.Add(new XyScatterRenderableSeriesViewModel()
{
DataSeries = AcquisitionPointDataSeries,
StyleKey = "ScatterSeriesStyle0"
});
}
#endregion
#region Public Properties
public ObservableCollection<IRenderableSeriesViewModel> TemperatureGraphViewModel { get; } = new ObservableCollection<IRenderableSeriesViewModel>();
public IViewportManager ViewportManager
{
get
{
return _viewportManager;
}
set
{
if (ReferenceEquals(value, _viewportManager))
{
return;
}
_viewportManager = (DefaultViewportManager)value;
OnPropertyChanged("ViewportManager");
}
}
public ICommand ZoomOutCommand
{
get
{
return _zoomOutCommand;
}
set
{
if (ReferenceEquals(value, _zoomOutCommand))
{
return;
}
_zoomOutCommand = value;
OnPropertyChanged(nameof(ZoomOutCommand));
}
}
#endregion
#region Public Methods
/// <summary>
/// To generate dummy data
/// // TODO: Need to integrate it with RunEditor with the actual data
/// </summary>
public void GenerateDummySeries()
{
double y = 80.5, yVar = 30.0;
TemperatureDataSeries.Append(TimeSpan.FromMinutes(1), 40.0);
TemperatureDataSeries.Append(TimeSpan.FromMinutes(2), 80.5);
for (int x = 2; x < 50; x++)
{
TemperatureDataSeries.Append(TimeSpan.FromMinutes(x), y);
yVar *= -1;
y += yVar;
}
for (var i = 5.4; i < 50; i += 2)
{
AcquisitionPointDataSeries.Append(TimeSpan.FromMinutes(i), 60.0);
}
}
public void ZoomOutTemperatureGrpah()
{
_viewportManager.ZoomExtents();
}
#endregion
}
}
This code is working fine and zooming out the scichart control to 100%.
I want to implement the same using the ZoomBy().

Since the methods Zoom, ZoomBy, Scroll and ScrollTo only exist on IAxis and AxisBase derived classes, the only way to trigger these from a ViewModel is to actually pass the Axis instance into your ViewModel.
This is not true MVVM but it could be considered a workaround: by passing your Axis as IAxis to the ViewModel and controlling it directly.
The only other way is to write a behavior, or SciChart's favourite: a ChartModifier, which listens to events from your ViewModel and controls the axis directly.
This is the preferred approach. If you search for 'Call method on View from ViewModel' you will see the approach of raising events on ViewModel and handling in the View.

Related

Calculate properties using items in List with conditions

I have a list object that has several properties that I want to use for calculations for an model properties.
something like:
List<Cars>
that has properties for Wheels/Windows/HasRoof/FuelType, etc.
I have a model for "Parts" (class example below) that I want to fill but I have a few rules to apply, I'm going to pseudoCode what I think I should do, but I'm not sure if this is the approach for this:
public class Parts
{
public int AmountOfWheels { get; set; }
public int AmountOfWheelsForFuelTypeGas { get; set; }
public AmountOfWindowsForCarsWithRoof Type { get; set; }
}
public Parts Parts { get; set; }
this is what I want to fill:
foreach (var item in Cars)
{
Parts.AmountOfWheels =+ item.Wheels;
Parts.AmountOfWheelsForFuelTypeGas // <-- This is what I don't know
Parts.AmountOfWindowsForCarsWithRoof // <-- This is what I don't know
}
Then later I want to show the user this Parts object in a webApp, but I'm not sure how to populate this object.
The part I'm not sure if it's ok to do the calculations like this or shall I do something in the object model with properties
I think I know what you're getting at, but tell me if I miss the mark!
For most UIs including web apps, the UI box that is displaying something like AmountOfWheels is bound to changes of that property using the INotifyPropertyChanged interface. If your Parts class implements that interface, then when this line in your code executes:
Parts.AmountOfWheels =+ item.Wheels;
that will change the value of AmountOfWheels property and that will fire a property changed event. The calculation in turn will set the other properties (like AmountOfWheelsForFuelTypeGas). That fires its own changed event and the UI just picks up on that property change and shows the value.
// A class that notifies when its properties change
class Part : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// The private 'backing store' of the variable which is a Field
/// </summary>
private int _amountOfWheels = 1;
/// <summary>
/// The public property
/// </summary>
public int AmountOfWheels
{
get => _amountOfWheels;
set
{
if (_amountOfWheels != value)
{
_amountOfWheels = value;
OnPropertyChanged();
}
}
}
int _amountOfWheelsForFuelTypeGas = -1;
public int AmountOfWheelsForFuelTypeGas
{
get => _amountOfWheelsForFuelTypeGas;
set
{
if (_amountOfWheelsForFuelTypeGas != value)
{
_amountOfWheelsForFuelTypeGas = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
switch (propertyName)
{
case nameof(AmountOfWheels):
onAmountOfWheelsChanged();
break;
}
}
private void onAmountOfWheelsChanged()
{
// Perform some calculation based on AmountOfWheels.
AmountOfWheelsForFuelTypeGas = TestDataGenerator.Next(0,10);
}
private Random TestDataGenerator = new Random();
}
Test driver for it:
class Program
{
static void Main(string[] args)
{
var part = new Part();
part.PropertyChanged += (sender, e) =>
{
switch (e.PropertyName)
{
case nameof(Part.AmountOfWheelsForFuelTypeGas):
Console.WriteLine($"Amount of Wheels {part.AmountOfWheels} = (Rando test data){part.AmountOfWheelsForFuelTypeGas}");
break;
}
};
for (int i = 0; i < 5; i++)
{
part.AmountOfWheels = i;
}
}
}

Create property dynamically in viewmodel MVVM light and UWP

I would like to create viewmodel properties in runtime.
I'm not so familiar with MVVM in UWP. Rather windows forms. In the past I created custom class object with reflection and I had possibility to add properties in runtime.
In current project I prepared solution with mvvm ligt and UWP app. Works fine with data exchange on viewmodel level. Now I try to find how to create properties of viewmodel in runtime ie. from descriptions in xml file.
namespace hmi_panel.ViewModels
{
public class HomeViewModel : ViewModelBase
{
#region Fields
readonly IPlcService _plcService;
#endregion
#region Constructors
public HomeViewModel(IPlcService dummyPlcService)
{
_plcService = dummyPlcService;
_plcService.Connect("127.0.0.1", 0, 1);
//zdarzenie cyklicznego odswiezania zeminnych
OnPlcServiceValuesRefreshed(null, null);
_plcService.ValuesRefreshed += OnPlcServiceValuesRefreshed;
}
#endregion
#region Properties
public string AppVersion
{
get { return $"{Package.Current.Id.Version.Major}.
{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}.
{Package.Current.Id.Version.Revision}"; }
}
public string AppCopyright
{
get { return "plc service: " + _plcService.ConnectionState.ToString(); }
}
private bool _pumpState;
public bool pumpState
{
get { return _pumpState; }
set {
_pumpState=value;
RaisePropertyChanged(() => pumpState);
}
}
#endregion
#region Methods
private RelayCommand _ConnectCommand;
public RelayCommand ConnectCommand
{
get
{
return _ConnectCommand ?? (_ConnectCommand = new RelayCommand(() =>
{
pumpState = true;
}, () => true));
}
}
private void OnPlcServiceValuesRefreshed(object sender, EventArgs e)
{
pumpState = _plcService.PumpState;
}
#endregion
}
}
Property pumpState value is readed and writed with _plService. I can change value and I can read after external change.
I would like to start only with bidirectional binding in xaml and create needed property ie. pumpState when viewmodel instance is created ie. in construtor.

How to access method from view inside a Xamarin Forms custom renderer?

I have the following code:
public partial class PhrasesFrameRendererClass : Frame
{
.....
void getRandomWords() {
// more code here that involves getting random numbers
// and updating a grid's bindingcontext
}
}
In my custom renderer I want to be able to call the getRandomWords on swipe left gesture like below:
public class PhraseFrameCustomRenderer : FrameRenderer
{
UISwipeGestureRecognizer leftSwipeGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
// Call getRandomWords() here
});
}
}
Is this possible? Any ideas on how this could be done?
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
// Call getRandomWords() here
var frame = Element as PhrasesFrameRendererClass ;
if(frame!=null){
frame.getRandomWords();
}
});
You can create a BindableProperty of type Command in your custom frame class, call that Command from your renderer and bind your ViewModel's getRandomWords method as a Command
//Your custom control in your PCL project
public partial class PhrasesFrameRendererClass : Frame
{
public static readonly BindableProperty SwipeLeftCommandProperty =
BindableProperty.Create(nameof(SwipeLeftCommand), typeof(ICommand), typeof(PhrasesFrameRendererClass ), null);
public ICommand SwipeLeftCommand
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
//Your custom control renderer
public class PhraseFrameCustomRenderer : FrameRenderer
{
UISwipeGestureRecognizer leftSwipeGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
leftSwipeGestureRecognizer = new UISwipeGestureRecognizer();
leftSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
leftSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
leftSwipeGestureRecognizer.AddTarget((obj) =>
{
var myFrame = Element as PhrasesFrameRendererClassl
if(myFrame != null){
if(myFrame.SwipeLeftCommand != null && myFrame.SwipeLeftCommand.CanExecute()){
myFrame.SwipeLeftCommand.Execute();
}
}
});
}
}
//Your ViewModel
public class PhrasesViewModel{
public Command GetRandomWordsCommand {get;set;}
public PhrasesViewModel(){
GetRandomWordsCommand = new Command(ExecuteGetRandomWords);
}
private void ExecuteGetRandomWords(){
//Your method goes here
}
}
//Your XAML
<yourControls:PhrasesFrameRendererClass SwipeLeftCommand="{Binding GetRandomWordsCommand }"/>
It may seem more complicated this way, but using commands allows you to separate your application code (Such as getting random phrases) from your rendering code

Notify ViewModel of Asynchronous changes in Model

I'm relatively new to MVVM and I'm wondering about the best way to structure my application. Here is a sample of my models:
public class ModelSource : ModelBase
{
#region Fields
private int _isLoading;
private BackgroundWorker worker = new BackgroundWorker();
private ObservableCollection<PCDatabase> _databases;
#endregion //Fields
#region Properties
public ObservableCollection<PCDatabase>Databases
{
get
{
if (_databases == null)
{
_databases = new ObservableCollection<PCDatabase>();
}
return _databases;
}
set
{
_databases = value;
this.OnPropertyChanged("Databases");
}
}
public int IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
OnPropertyChanged("IsLoading");
}
}
#endregion
#region Methods
/// <summary>
/// Gets all Databases from the Server
/// </summary>
public void getDatabasesAsync(ConfigDatabaseConnection _currentConfig)
{
//execute SQL Query...
}
(ModelBase implements INotifyPropertyChanged).
Here is my corresponding ViewModel:
namespace DbRestore.ViewModel
{
public class ViewModelSource : ViewModelBase
{
private ObservableCollection<PCDatabase> _databases;
private ModelSource _modelSource;
private ICommand _populateDatabaseCommand;
public ConfigDatabaseConnection _currentConfig;
public ViewModelSource()
{
this.ModelSource = new ModelSource();
}
#region Commands
/// <summary>
/// Command that opens a Database Connection Dialog
/// </summary>
public ICommand OpenDataBaseConnectionCommand
{
get
{
if (_populateDatabaseCommand == null)
{
_populateDatabaseCommand = new RelayCommand(
param => this.PopulateDatabases()
);
}
return _populateDatabaseCommand;
}
}
public ObservableCollection<PCDatabase> Databases
{
get
{
return _databases;
}
set
{
_databases = value;
OnPropertyChanged("Databases");
}
}
#endregion //Commands
public void PopulateDatabases()
{
ModelSource.getDatabasesAsync(_currentConfig);
}
Calling ModelSource.getDatabasesAsync(_currentConfig) gets my SQL Data in my model. Due to some of my SQL queries being quite complex, I've implemented a Background Worker that runs these queries asynchronously.
How do I get the data into my ViewModel, which is bound to my View? Or is my design approach as a whole faulty?
Things I've considered and tried:
Binding directly to the model: Works, but I've been told that this is a
bad practice, and the application logic should reside in the Model.
Moving the SQL queries into the ViewModel: Also works, but then my Model
class seems to be redundant - it would be nothing but a custom datatype.
Run the queries synchronously and directly assign the Observable
Collection in my model to the Observable Collection in my ViewModel. Also
works, but then I'm running into problems with my BackgroundWorker,
because the ViewModel won't know when the Query is actually finished.
Move all your database logic into a service (aka repository) class.
It is OK to bind directly to the model properties instead of creating a dozen ViewModel proxy classes for each Model, as soon as you don't need any special view-related logic around a particular model. So exposing a collection of PCDatabase is OK.
Since you're using BackgroundWorker, I assume you use .NET Framework 3.5 and don't have TPL.
public interface IPCDatabaseRepository
{
void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler);
}
public class PCDatabaseRepository : IPCDatabaseRepository
{
public void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler)
{
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
args.Result = // Execute SQL query...
};
worker.RunWorkerCompleted += (sender, args) =>
{
resultHandler(args.Result as IList<PCDatabase>);
worker.Dispose();
};
worker.RunWorkerAsync();
}
}
public class ViewModelSource : ViewModelBase
{
private readonly IPCDatabaseRepository _databaseRepository;
private ObservableCollection<PCDatabase> _databases;
private bool _isBusy;
public ViewModelSource(IPCDatabaseRepository databaseRepository /*Dependency injection goes here*/)
{
_databaseRepository = databaseRepository;
LoadDatabasesCommand = new RelayCommand(LoadDatabases, () => !IsBusy);
}
public ICommand LoadDatabasesCommand { get; private set; }
public ObservableCollection<PCDatabase> Databases
{
get { return _databases; }
set { _databases = value; OnPropertyChanged("Databases"); }
}
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; OnPropertyChanged("IsBusy"); CommandManager.InvalidateRequerySuggested(); }
}
public void LoadDatabases()
{
IsBusy = true;
_databaseRepository.GetPCDatabasesAsync(results =>
{
Databases = new ObservableCollection(results);
IsBusy = false;
});
}
Have you seen these articles?
Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding
https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
Async Programming : Patterns for Asynchronous MVVM Applications: Commands
https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
These should cover a good strategy especially when working with async/await.

ListBox ItemsSource Doesn't Update

I am facing a ListBox's ItemsSource related issue. I am implementing MVVM with WPF MVVM toolkit version 0.1.
I set one ListBox itemSource to update when a user double clicks on some other element (I handled the event in the code behind and executed the command there, since binding a command to specific events are not supported). At this point through the execution of the command a new ObservableCollection of items get generated and the ListBox's ItemsSource is intended to get updated accordingly. But it is not happening at the moment. ListBox does not update dynamically. What can be the problem? I am attaching relvent code for your reference.
XAML:
List of items which is doubled click to generate the next list:
<ListBox Height="162" HorizontalAlignment="Left" Margin="10,38,0,0" Name="tablesViewList" VerticalAlignment="Top" Width="144" Background="Transparent" BorderBrush="#20EEE2E2" BorderThickness="5" Foreground="White" ItemsSource="{Binding Path=Tables}" SelectedValue="{Binding TableNameSelected, Mode=OneWayToSource}" MouseDoubleClick="tablesViewList_MouseDoubleClick"/>
Second list of items which currently does not get updated:
<ListBox Height="153" HorizontalAlignment="Left" Margin="10,233,0,0" Name="columnList" VerticalAlignment="Top" Width="144" Background="Transparent" BorderBrush="#20EEE2E2" BorderThickness="5" Foreground="White" ItemsSource="{Binding Path=Columns, Mode=OneWay}" DisplayMemberPath="ColumnDiscriptor"></ListBox>
Code Behind:
private void tablesViewList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
MainViewModel currentViewModel = (MainViewModel)DataContext;
MessageBox.Show("Before event command is executed");
ICommand command = currentViewModel.PopulateColumns;
command.Execute(null);
MessageBox.Show(currentViewModel.TableNameSelected);
//command.Execute();
}
View Model:
namespace QueryBuilderMVVM.ViewModels
{
//delegate void Del();
public class MainViewModel : ViewModelBase
{
private DelegateCommand exitCommand;
#region Constructor
private ColumnsModel _columns;
public TablesModel Tables { get; set; }
public ControllersModel Operators { get; set; }
public ColumnsModel Columns {
get { return _columns; }
set {
_columns = value;
OnPropertyChanged("Columns");
}
}
public string TableNameSelected{get; set;}
public MainViewModel()
{
Tables = TablesModel.Current;
Operators = ControllersModel.Current;
Columns = ColumnsModel.ListOfColumns;
}
#endregion
public ICommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand = new DelegateCommand(Exit);
}
return exitCommand;
}
}
private void Exit()
{
Application.Current.Shutdown();
}
//Del columnsPopulateDelegate = new MainViewModel().GetColumns;
//Method to be assigned to the delegate
//Creates an object of type ColumnsModel
private void GetColumns() {
ColumnsModel.TableNameParam = TableNameSelected;
Columns = ColumnsModel.ListOfColumns;
}
private ICommand _PopulateColumns;
public ICommand PopulateColumns
{
get {
if (_PopulateColumns == null) {
_PopulateColumns = new DelegateCommand(GetColumns); // an action of type method is passed
}
return _PopulateColumns;
}
}
}
}
Model:
public class ColumnsModel : ObservableCollection<VisualQueryObject>
{
private DataSourceMetaDataRetriever dataSourceTableMetadataObject;// base object to retrieve sql data
private static ColumnsModel listOfColumns = null;
private static object _threadLock = new Object();
private static string tableNameParam = null;
public static string TableNameParam
{
get { return ColumnsModel.tableNameParam; }
set { ColumnsModel.tableNameParam = value; }
}
public static ColumnsModel ListOfColumns
{
get
{
lock (_threadLock)
if (tableNameParam != null)
listOfColumns = new ColumnsModel(tableNameParam);
return listOfColumns;
}
}
public ColumnsModel(string tableName)
{
ColumnsModel.tableNameParam = tableName;
Clear();
try
{
dataSourceTableMetadataObject = new DataSourceMetaDataRetriever();
List<ColumnDescriptionObject> columnsInTable = new List<ColumnDescriptionObject>();
columnsInTable = dataSourceTableMetadataObject.getDataTableSchema("Provider=SQLOLEDB;Data Source=.;Integrated Security=SSPI;Initial Catalog=LogiwizUser", ColumnsModel.tableNameParam);
//List<String> listOfTables = dataSourceTableMetadataObject.getDataBaseSchema("Provider=SQLOLEDB;Data Source=.;Integrated Security=SSPI;Initial Catalog=LogiwizUser");
//List<String> listOfTables = dsm.getDataBaseSchema("G:/mytestexcel.xlsx", true);
//ObservableCollection<VisualQueryObject> columnVisualQueryObjects = new ObservableCollection<VisualQueryObject>();
foreach (ColumnDescriptionObject columnDescription in columnsInTable)
{
VisualQueryObject columnVisual = new VisualQueryObject();
columnVisual.ColumnDiscriptor = columnDescription;
columnVisual.LabelType = "column";
Add(columnVisual);
}
}
catch (QueryBuilderException ex)
{
/* Label exceptionLabel = new Label();
exceptionLabel.Foreground = Brushes.White;
exceptionLabel.Content = ex.ExceptionMessage;
grid1.Children.Add(exceptionLabel);*/
}
}
}
Any help is greatly appreciated. Thanks in advance.
The setter of property Columns should raise a PropertyChanged event.
Implement INotifyPropertyChanged to do so : MSDN INotifyPropertyChanged
I guess MVVM Toolkit provides a way of doing so easily (perhaps ViewModelBase already implement the interface ...).
EDIT : Implementing INotifyPropertyChanged is not enough, you have to raise the event created by INotifyPropertyChanged. You property should look something like this :
private ColumnsModel _columns;
public ColumnsModel Columns
{
get { return _columns; }
set
{
_columns = value;
PropertyChanged("Columns");
}
}
use an observableCollection<T> instead of a List<T>
MSDN DOC:
WPF provides the ObservableCollection class, which is a built-in implementation of a data collection that exposes the INotifyCollectionChanged interface. Note that to fully support transferring data values from source objects to targets, each object in your collection that supports bindable properties must also implement the INotifyPropertyChanged interface. For more information, see Binding Sources Overview.

Categories

Resources