I have a problem with refreshing Oxyplot graph in Xamarin Android. I'm using Model.InvalidatePlot(true) but still doesnt work.
In my ViewModel I have property PlotModel and method for Refresh like code below. GroupSales is my MvxObservableCollection of GroupSale.
public void RefreshTest()
{
GroupSales.Clear();
var groupSales = DemoData.GetGroupSaleList(_shop.Id);
groupSales.Add(new GroupSale()
{
Name = "Test",
Sum = 5555,
Color = Constants.DefaultColor
});
foreach (var item in groupSales)
{
GroupSales.Add(item);
}
Model = OxyPlotModelCreator.GetGroupSalesModel(GroupSales);
Model.InvalidatePlot(true);
}
private PlotModel _model;
public PlotModel Model
{
get { return _model; }
set
{
_model = value;
RaisePropertyChanged(() => Model);
}
}
After call method RefreshTest my MvxObservableCollection is updated and Model too but still looking same in view. Only when Im change mobile orientation its update (cuz Im using two views for portrait and landscape so its initialized again) but I need refresh PlotModel after click on Refresh button.
I tried already call this method in Android fragment and Invalidete PlotView and Model like this:
Button button = _view.FindViewById<Button>(Resource.Id.refreshButton);
button.Click += delegate
{
ViewModel.RefreshTest();
if (plotView != null && ViewModel.Model != null)
{
plotView.InvalidatePlot(true);
plotView.Invalidate();
plotView.RefreshDrawableState();
plotView.Model.InvalidatePlot(true);
}
};
But still doesnt work.... Can someone help me?
EDIT
I Initialize Model in Fragment like this:
var plotView = _view.FindViewById<PlotView>(Resource.Id.groupSalesModel);
if (plotView != null && ViewModel.Model != null)
{
plotView.Model = ViewModel.Model;
}
Oh I got it... I just bind in fragment like this:
var bindset = this.CreateBindingSet<GroupSalesFragment, GroupSalesViewModel>();
bindset.Bind(plotView).For(c => c.Model).To(vm => vm.Model);
bindset.Apply();
And its working now....
Related
I am working on a new UWP application that interacts with some hardware via Bluetooth. Using the windows-universal-samples repo as a guide I was able to sucessfully get what I wanted working.
Now I am trying to refactor the code I wrote in a click event handler into a view model class using Prism. However I don't know how to approach this. In other scenarios where I need to pass data between a View and ViewModel I would create a property on the ViewModel and bind it to the control in the view's XAML.
The problem is that Windows.Devices.Enumaration.DevicePicker is used in a way that doesn't seem compatible with the MVVM pattern. In the click handler, the data and control are merged together and I don't see how I can make some kind of list property on the view model and then bind it to the view. Here is the simplest example of the code I am working with:
async void DiscoverButton_OnClick(object sender, RoutedEventArgs e)
{
var devicePicker = new DevicePicker();
devicePicker.Filter.SupportedDeviceSelectors.Add(BluetoothLEDevice.GetDeviceSelectorFromPairingState(true));
// Calculate the position to show the picker (right below the buttons)
var ge = DiscoverButton.TransformToVisual(null);
var point = ge.TransformPoint(new Point());
var rect = new Rect(point, new Point(100, 100));
var device = await devicePicker.PickSingleDeviceAsync(rect);
var bluetoothLEDevice = await BluetoothLEDevice.FromIdAsync(device.Id);
}
See PickSingleDeviceAsync() creates a control directly.
Now I am trying to refactor the code I wrote in a click event handler into a view model class using Prism. However I don't know how to approach this.
You could bind command for your button and use CommandParameter to pass parameter to the command.
Please refer to the following code sample for details:
<Button x:Name="btn" Content="device" Command="{Binding ClickCommand}" CommandParameter="{Binding ElementName=btn}"></Button>
public class MianViewModel : BindableBase
{
public ICommand ClickCommand
{
get;
private set;
}
public MianViewModel()
{
ClickCommand = new DelegateCommand<object>(ClickedMethod);
}
private async void ClickedMethod(object obj)
{
var devicePicker = new DevicePicker();
devicePicker.Filter.SupportedDeviceSelectors.Add(BluetoothLEDevice.GetDeviceSelectorFromPairingState(true));
// Calculate the position to show the picker (right below the buttons)
Button DiscoverButton = obj as Button;
if (DiscoverButton != null)
{
var ge = DiscoverButton.TransformToVisual(null);
var point = ge.TransformPoint(new Point());
var rect = new Rect(point, new Point(100, 100));
var device = await devicePicker.PickSingleDeviceAsync(rect);
if (device != null)
{
var bluetoothLEDevice = await BluetoothLEDevice.FromIdAsync(device.Id);
}
}
}
}
The solution I came up with was to abandon the built in UI provided by DevicePicker and instead create my own UI to use with DeviceWatcher. For example:
void StartWatcher()
{
ResultCollection.Clear();
string selector = BluetoothLEDevice.GetDeviceSelector();
DeviceWatcher = DeviceInformation.CreateWatcher(selector);
DeviceWatcher.Added += async (deviceWatcher, deviceInformation) =>
{
await OnUiThread(() =>
{
ResultCollection.Add(deviceInformation);
});
};
DeviceWatcher.Start();
}
Where ResultCollection would be bound from the view model to the view.
I'm trying to use Catel ViewManager to registering and getting view but I don't get View from ViewManager.GetViewsOfViewModel
As I understand it, a when the UserControl is unloaded from the visual tree it is destroyed but this should not happen becouse I use CloseViewModelOnUnloaded = false;
I made a small example to demonstrate this problem. TestApp
public void ShowDocumentWindow<TViewModel>(object model = null) where TViewModel : DocumentItemViewModel
{
IViewModel vm = null;
if (model != null)
{
vm = this.viewModelManager.GetViewModelsOfModel(model).FirstOrDefault();
}
if (vm == null)
{
var viewModel = this.viewModelFactory.CreateViewModel<TViewModel>(model);
var viewType = this.viewLocator.ResolveView(viewModel.GetType());
var view = Activator.CreateInstance(viewType, viewModel);
this.documentHost.Items.Add(new DocumentPanel
{
Content = view,
IsActive = true,
MDIState = MDIState.Normal
});
this.viewManager.RegisterView((IView)view); // Registering
this.viewModelManager.RegisterModel(viewModel, model);
}
else
{
var view = this.viewManager.GetViewsOfViewModel(vm).FirstOrDefault() as UserControl; \\ but this I get null
var index = this.documentHost.Items.FindIndex(x =>
{
var documentPanel = x as DocumentPanel;
if (documentPanel != null)
{
return Equals(documentPanel.Content, view);
}
return false;
});
this.documentHost.SelectedTabIndex = index;
}
}
The view gets removed from the "active views" (inside the ViewManager) as soon as it is unloaded. That it keeps its view model in memory for when it is being loaded again doesn't change the fact that the view is currently not being used.
One solution is to create your own IViewManager (everything is pluggable in Catel), then you can somehow create a "threshold period" where views are not really removed so they have a chance to be reloaded.
But... I think it's better to store state inside a service / memory object and restore that and create a new VM when you load the view again.
I'm new to WP8 and follow many tutorials. For parts of the menu I use a viewModel with NotifyPropertyChanged. When I get my list of news articles it creates a viewModel and displays it in a longListSelector.
But also I want to make 1 HubTile with the image and some preview-text of the first article. Is there a nice way to send some event to the .xaml.cs? Or do I have to make another viewModel for this one HubTile and make a binding?
Ony try was to make such a variable:
private bool _isDataLoaded = false;
public bool IsDataLoaded
{
get
{
return _isDataLoaded;
}
set
{
if (value != _isDataLoaded)
{
_isDataLoaded = value;
NotifyPropertyChanged("IsDataLoaded");
}
}
}
The same thing is used with "IsLoading"-variable to create a loading-indicator in the systemTray:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("MainPage_Loaded-Funktion");
Binding binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsVisibleProperty, binding);
binding = new Binding("IsLoading") { Source = DataContext };
BindingOperations.SetBinding(
prog, ProgressIndicator.IsIndeterminateProperty, binding);
prog.Text = "Lade aktuelle Inhalte...";
}
Can I use this to call a function, when my variable is set and I get a notification?
The solution that helped me out was this:
<toolkit:HubTile Message="{Binding OnlineNews[0].TeaserText}"/>
Didn't know that you can access the viewModel like that. Thanks to Toni Petrina!
I am using WCF Services
I have this problem:
When I retrieve data from server for my GridView at the start of an async function call, I set IsBusy = "True" . After the method is called, I set IsBusy = "False". During the method call RadBusyIndicator does not Display. I cannot understand what the problem is.
I have uploaded a simple project with this problem. Can you check it? Download
I moved the loading in a BackgroundWorker, can you try this:
private void LoadData()
{
//Activate BudyIndicator
App.Instance.SetBusy();
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
ObservableCollection<CustomerModel> LoadedCustomers = null;
//Create Client
proxy.ServicesClient client = new proxy.ServicesClient();
ObservableCollection<Customer> customers = client.GetCustomers();
LoadedCustomers = new ObservableCollection<CustomerModel>();
foreach (var item in customers)
{
LoadedCustomers.Add(new CustomerModel()
{
CustomerId = item.CustomerId,
Title = item.Title,
FirstName = item.FirstName,
MiddleName = item.MiddleName,
LastName = item.LastName,
CompanyName = item.CompanyName,
SalesPerson = item.SalesPerson,
EmailAddress = item.EmailAddress,
Phone = item.Phone
});
}
client.Close();
//Define return value
ea.Result = LoadedCustomers;
};
worker.RunWorkerCompleted += (o, ea) =>
{
//Get returned value
ObservableCollection<CustomerModel> model = ea.Result as ObservableCollection<CustomerModel>;
if (model != null)
{
Customers = model;
}
//Desactivate BusyIndicator
App.Instance.UnSetBusy();
};
worker.RunWorkerAsync();
}
Ok I see your problem. The Close method on your proxy wait the result of the asynch call.
Just move your client.Close(); in the GetCustomersCompleted method and this will work. (Tested with your sample)
private proxy.ServicesClient client = null;
private void LoadData()
{
App.Instance.SetBusy();
client = new proxy.ServicesClient();
client.GetCustomersCompleted += (s, e) =>
{
if (e.Error != null)
{
throw new Exception();
}
else
{
Customers = new ObservableCollection<CustomerModel>();
foreach (var item in e.Result)
{
Customers.Add(new CustomerModel()
{
CustomerId = item.CustomerId,
Title = item.Title,
FirstName = item.FirstName,
MiddleName = item.MiddleName,
LastName = item.LastName,
CompanyName = item.CompanyName,
SalesPerson = item.SalesPerson,
EmailAddress = item.EmailAddress,
Phone = item.Phone
});
}
OnPropertyChanged("Customers");
}
client.Close();//Close after the return
App.Instance.UnSetBusy();
};
client.GetCustomersAsync();
//client.Close();
}
}
If your window xaml is not inside the busy indicator it may not show. With that control you need to put the content you wish to be masked when the busy indicator is set to true inside the indicator tags. If you UserControl's primary display item is a Grid then wrap the grid in the busy indicator tags.
<UserControl>
<telerik:RadBusyIndicator IsBusy={Binding Busy}>
<Grid>
content...
</Grid>
</telerik:RadBusyIndicator>
</UserControl>
This should give you the result you are looking for.
If you are using a binding for the IsBusy property on the controller, which I assume you are, then you must implement the INotifyPropertyChanged interface so that when the value of the binding property is changed the UI is notified of that change and updates itself.
Your view model should have a property with a setter as follows:
public bool Busy
{
get{return _Busy;}
set
{
if(value != _Busy)
_Busy = value;
OnPropertyChanged("Busy");
}
}
This will notify the UI of the change;
If you are doing this already then I will need to see more of the relevant code to help more.
After I looked at your last post again if you are setting the IsBusy property to a string value that is your problem as that property takes a Boolean value.
I assume based on your code you wish to only put the busy indicator over the content control on the main window. My recommendation is to create a view model for the main window and use it as the datacontext for the page. I would also set up a property on the view model as explained above and set up a binding to that property. In the view model you can make the async call to the data store and on the return fill a collection property (recommend ObservableCollection) and bind your ListBox's IitemsSource property to that.
I hope this helpls
I'm using WPF to create an application to enable an organisation to enter different pieces of data into the application.I have a tab control to allow them to do this.
Then in a separate view, I have a series of different data grids showing the user what data they have inserted into the database. Containing buttons to either, add, update or delete the data they want.
Which leads me to my question. Currently, I am able to delete, and add data with ease and with no problem. But then comes my issue with trying to get the selected item to update, which it doesn't, resulting in a null reference exception.
If i set my property attributes programmatically though, it updates it fine. like so;public int _OrganisationTypeDetailID = 17; public int _OrganisationTypeID = 1;But I do not want this, as I want the ability for the user to select for themselves and update the data they need to.
Here's some of the code that may help in resolving my issue;
View Model;
public void UpdateOrganisationTypeDetail(OrganisationTypeDetail orgTypeDetail)
{
using (DBEntities context = new DBEntities())
{
var orgTD = context.OrganisationTypeDetails.Where(otd => otd.OrganisationTypeDetailID == SelectedType.OrganisationTypeDetailID).FirstOrDefault();
if (orgTD != null)
{
orgTD.Title = Title;
orgTD.FirstName = FirstName;
orgTD.Surname = Surname;
orgTD.Position = Position;
orgTD.DateOfBirth = DateOfBirth;
orgTD.Address = Address;
orgTD.Country = Country;
orgTD.Postcode = Postcode;
orgTD.PhoneNumber = PhoneNumber;
orgTD.MobileNumber = MobileNumber;
orgTD.FaxNumber = FaxNumber;
orgTD.Email = Email;
orgTD.NINumber = NINumber;
//context.OrganisationTypeDetails.Attach(orgTD);
context.OrganisationTypeDetails.ApplyCurrentValues(orgTD);
context.SaveChanges();
MessageBox.Show("Updated Organisation Type Details");
}
else
{
MessageBox.Show("Unable to update selected 'Type'.");
}
}
private OrganisationTypeDetail _SelectedType;
public OrganisationTypeDetail SelectedType
{
get
{
return _SelectedType;
}
set
{
if (_SelectedType == value)
return;
_SelectedType = value;
OnPropertyChanged("SelectedType");
}
}
public List<OrganisationTypeDetail> GetOrganisationTypeDetail //Loads data
{
get
{
using (DBEntities context = new DBEntities())
{
var query = from e in context.OrganisationTypeDetails
select e;
return query.ToList<OrganisationTypeDetail>();
}
}
}
private ICommand showUpdateCommand;
public ICommand ShowUpdateCommand //Update command
{
get
{
if (showUpdateCommand == null)
{
showUpdateCommand = new RelayCommand(this.UpdateFormExecute, this.UpdateFormCanExecute); //i => this.UpdateOrganisationTypeDetail()
}
return showUpdateCommand;
}
}
Code behind;
private void btnUpdateOrgTypeDetail_Click(object sender, RoutedEventArgs e)
{
OrganisationTypeDetail selected = dgOrgTypeDetail.SelectedItem as OrganisationTypeDetail;
OrganisationTypeDetailViewModel org = new OrganisationTypeDetailViewModel();
if (selected == null)
MessageBox.Show("You must select a 'Type' before updating.");
else
{
OrganisationTypeDetailUpdateView update = new OrganisationTypeDetailUpdateView();
update.ShowDialog();
org.UpdateOrganisationTypeDetail(selected);
Page_Loaded(null, null);
}
}
xaml;
<DataGrid Name="dgOrgTypeDetail" Height="145" Width="555"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding GetOrganisationTypeDetail}"
SelectedItem="{Binding SelectedType, Mode=TwoWay}">
Hope this issue can be resolved.
I would say that your best bet for this is to use commanding in the MVVM pattern to achieve this..
It looks like you're using a combination of MVVM and code behind and actually creating a new instance of the view model when your click event fires. Try binding the view model to your view once in the code behind of the view as the datacontext and then try updating the selected type..
Also when you're trying to do the update on SelectedType - look at your View using Snoop - see if the SelectedType property is still bound to the view.
ICommand UpdateOrgTypeDetail { get;}
Then in the view model constructor declare new instance
UpdateOrgTypeDetail = new DelegateCommand<object>(ExecuteUpdateOrgTypeDetail, CanExecuteUpdateOrgTypeDetail);
These two delegates will then allow you to click your button (which needs to bind to UpdateOrgTypeDetail)
<Button Command="{Binding UpdateOrgTypeDetail}" />
You should find that the update on the property is done correctly from here.