I am trying to get a sample to work using MVVM Light and the Messaging Class. In the sample, I have a test project created from the MVVM Template for Silveright 4. I have added a button on the main page. When the button is clicked, it updates a property on the ViewModel. When the property is updated, I want to show a messagebox with the new value.
The key line of code is:
Messenger.Default.Register(this, new Action(ShowMessage));
I can get this to work in WPF, but not silverlight. It should call ShowMessage with the string parameter when the property changes, but it does not. If I use:
Messenger.Default.Send("Hello MVVM");
This works and the string is sent as a message to ShowMessage.
However, the message does not get sent if the property changes, even though the property was created with the MVVMINPC snippet and has the following line:
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
This should have the same effect as Messager.Default.Send but it seems to be ignored. ThePropertyChangedEvent is indeed raised, but the messanger part seems to be disconnected.
Am I doing something wrong? Here is the full MainViewModel:
public class MainViewModel : ViewModelBase
{
public RelayCommand MyRelayCommand { get; set; }
public const string MyPropertyPropertyName = "MyProperty";
private string _myProperty = "test";
public string MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
var oldValue = _myProperty;
_myProperty = value;
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
public void DoSomething()
{
//Messenger.Default.Send("Hello MVVM"); //Works
this.MyProperty = "Hello World"; //Doesn't work.
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
Messenger.Default.Register(this, new Action<string>(ShowMessage));
MyRelayCommand = new RelayCommand(new Action(DoSomething));
this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);
}
void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
MessageBox.Show(e.PropertyName);
}
}public class MainViewModel : ViewModelBase
{
public RelayCommand MyRelayCommand { get; set; }
public const string MyPropertyPropertyName = "MyProperty";
private string _myProperty = "test";
public string MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
var oldValue = _myProperty;
_myProperty = value;
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
public void DoSomething()
{
//Messenger.Default.Send("Hello MVVM"); //Works
this.MyProperty = "Hello World"; //Doesn't work.
}
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
Messenger.Default.Register(this, new Action<string>(ShowMessage));
MyRelayCommand = new RelayCommand(new Action(DoSomething));
this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(MainViewModel_PropertyChanged);
}
void MainViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
MessageBox.Show(e.PropertyName);
}
}v
OK, I found that the Register line should look like this:
Messenger.Default.Register(this, new Action<PropertyChangedMessage<string>>(ShowMessage));
The point being there are different types of messages, and you have to register the PropertyChangedMessage type to recieve property changed messages.
Then also, the Action that recieves the message needs to take the correct parameter, like this:
public void ShowMessage(PropertyChangedMessage<string> e)
{
MessageBox.Show(e.NewValue.ToString());
}
Related
I am trying to push a view in xamrian forms from the view model but I cant appear to get it to work its really when the user has entered correct username and password it should show the home page.
You will see I have the on submit command this is just mock data at present so dont mind the design of code at this stage will change.
Usually I would use
var stocktakepage = new StockTake();
await Navigation.PushAsync(stocktakepage);
But the model does not no about the navigation stack in the class is their another way to navigate from the view model thanks.
public class LoginViewModel : INotifyPropertyChanged
{
public Action DisplayInvalidLoginPrompt;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string email;
public string Email
{
get { return email; }
set
{
email = value;
PropertyChanged(this, new PropertyChangedEventArgs("Email"));
}
}
private string password;
public string Password
{
get { return password; }
set
{
password = value;
PropertyChanged(this, new PropertyChangedEventArgs("Password"));
}
}
public ICommand SubmitCommand { protected set; get; }
public LoginViewModel()
{
SubmitCommand = new Command(OnSubmit);
}
public void OnSubmit()
{
if (email != "handheld1" || password != "test123")
{
DisplayInvalidLoginPrompt();
}else
{
}
}
}
I found the answer here
https://forums.xamarin.com/discussion/21822/call-navigation-pushasync-from-viewmodel But also on my main login page i have this. What this does is act like a delegate and allows you to push the view from the original calling page.
public Login()
{
var vm = new LoginViewModel();
this.BindingContext = vm;
Password.Completed += (object sender, EventArgs e) =>
{
vm.SubmitCommand.Execute(null);
};
}
You can also use the below in order to Navigate from your ViewModel. You can do this for each type of page you want. Check below examples:
await App.Current.MainPage.Navigation.PushAsync(new PageName());
also
await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new PageName()) { BarBackgroundColor = Color.FromHex("#101010"), BarTextColor = Color.White, }, true);
On selection of Tableview row, it display the data in the combobox,
i try to display the value in the comboxbox from tableview using this code.
private void showInputDetails(InputConfigurationModel ipmodel) {
if (ipmodel != null) {
cmbfiletype.setValue(ipmodel.getFiletype().toString());
}
My Inputconfiguartion Model class:
public class InputConfigurationModel {
public StringProperty filetype = new SimpleStringProperty();
public InputConfigurationModel(String filetype) {
this.filetype = new SimpleStringProperty(filetype);
}
public InputConfigurationModel() {
}
public String getFiletype() {
return filetype.get();
}
public void setFiletype(String value) {
filetype.set(value);
}
public StringProperty filetypeProperty()
{
return filetype;
}
// #Override
// public String toString() {
// return filetype.toString(); //To change body of generated methods, choose Tools | Templates.
// }
}
but if i try change the value of combobox for update it display error as
Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: java.lang.String cannot be cast to Clover.Converter.Model.ExtensionModel
i am trying to change the value in the combobox from this code
Loading the combobox value from this Loadfile method
public void LoadFile() {
try {
String sql = "select * from EXTENSION";
PreparedStatement ps = conn.prepareStatement(sql);
rs = ps.executeQuery(sql);
while (rs.next()) {
ExtensionModel extModel = new ExtensionModel();
filetypelstupdate_ip = FXCollections.observableArrayList(extModel);
String ext_type = rs.getString("EXT_TYPE");
String ext_name = rs.getString("EXT_NAME");
int ext_id=Integer.parseInt(rs.getString("EXT_ID"));
extModel.setExttype(ext_type);
extModel.setExtname(ext_name);
extModel.setExtid(ext_id);
cmbfiletype.getItems().addAll(filetypelstupdate_ip);
cmbfiletype.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ExtensionModel>() {
#Override
public void changed(ObservableValue<? extends ExtensionModel> observable, ExtensionModel oldValue, ExtensionModel newValue) {
aaa(newValue);
}
});
catch (Exception ex) {
ex.printStackTrace();
}
}
public void aaa(ExtensionModel exm){
if(cmbfiletype!=null)
{
txtextid.setText(exm.getExtid().toString());
System.out.println("txtextid"+txtextid);
}
}
i dont how to solve this error? tried everything but still coming class cast exception
my error is coming in LoadFile Method in this code first line
**cmbfiletype.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<ExtensionModel>() {**
#Override
public void changed(ObservableValue<? extends ExtensionModel> observable, ExtensionModel oldValue, ExtensionModel newValue) {
aaa(newValue);
});
}
} catch (Exception e) {
e.printStackTrace();
}
First it display the value in the comboxbox from the tableview which is a string in INPUTCONFIGURATION model class and thn i tried to change the the combobox value which comes from LoadFile method, hence it comes as class cast exception. kindly help me how to solve this class cast exception error.
I am getting data from a web service and I am loading it in Picker. Now I want to call a new web service to get some data related to selected item. But I am not getting that selected item.
I am using below class model to get data from web service and loading it in Picker.
public class ModelGetEmployeeList
{
public string ServiceStatus { get; set; }
public List<EmployeeList> EmpList { get; set; }
}
public class EmployeeList
{
public string uid { get; set; }
public string fname { get; set; }
public string lname { get; set; }
}
This is how I loaded data in Picker:
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync ();
var Items = JsonConvert.DeserializeObject <ModelGetEmployeeList> (content);
foreach(EmployeeList emp in Items.EmpList)
{
pickerEmployee.Items.Add(emp.uid.ToString()+"-"+emp.fname.ToString()+" "+emp.lname.ToString());
}
}
Now I am implementing SelectedIndexChanged event like this:
public void PickerEmployee_SelectedIndexChanged(object sender, SelectedItemChangedEventArgs e)
{
if (pickerEmployee.SelectedIndex == -1)
{
//Message
}
else
{
var item = sender as EmployeeList;
var selectedItem = item.uid;
DisplayAlert (selectedItem.ToString (), "OK", "OK");
}
}
But its giving me an error that above method has wrong signature.
You can take sellected value with this:
string selectedEmployee = string.Empty;
selectedEmployee = pickerEmployee.Items[pickerEmployee.SelectedIndex];
According to the Xamarin.Forms Picker documentation SelectedIndexChanged event is expecting delegate which matches EventHandler delegate (EventHandler documentation)
So, you have to change signature of your method :
public void PickerEmployee_SelectedIndexChanged(object sender, EventArgs e)
{
...
}
Your signature is wrong.
Also the following code is wrong:
var item = sender as EmployeeList;
var selectedItem = item.uid;
Please find the corrected version below :
public void PickerEmployee_SelectedIndexChanged(object sender, EventArgs e)
{
if (pickerEmployee.SelectedIndex == -1)
{
//Message
}
else
{
var selectedItem = PickerEmployee[SelectedIndex];
DisplayAlert (selectedItem, "OK", "OK");
}
}
The Xamarin Forms picker will get you only the string which was added to the list and not the object.
If you need the object either you can use the selectedIdex on your orginal lsit to get the object as :
var selectedEmp = Items.EmpList[SelectedIndex];
Or you can use a Bindable Picker.
public void PickerEmployee_SelectedIndexChanged(object sender, EventArgs e)
{
if (pickerEmployee.SelectedIndex == -1)
{
//Message
}
else
{
var selectedItem = (EmployeeList)PickerEmployee.SelectedItem;
DisplayAlert (selectedItem.fname, "OK", "OK");
}
}
public void PickerEmployee_SelectedIndexChanged(object sender, SelectedItemChangedEventArgs e)
{
if (pickerEmployee.SelectedIndex == -1)
{
//Message
}
else
{
var item = sender as Picker;
var selectedItem = item.SelectedItem as EmployeeList;
var uid =selectedItem.uid;
DisplayAlert (uid .ToString (), "OK", "OK");
}
}
The Items collection is a list of strings so you can get the currently selected value using SelectedIndex
var selectedValue = picker.Items [picker.SelectedIndex];
If you are using binding then yes, the exposed property is the SelectedIndex.
For more info click here
//How to get value of picker in Xamarin forms
//We are getting Text and Value from API
//xaml page
<controls:BorderlessPicker
x:Name="Pickdoctype"
ItemDisplayBinding="{Binding text}"
SelectedIndexChanged="Pickdoctype_SelectedIndexChanged"
HorizontalOptions="FillAndExpand"
Title="Enter Document Type"
FontSize="20"
TextColor="Gray">
</controls:BorderlessPicker>
// xaml.cs page
private void Pickdoctype_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
DocumentTypeModel selectedItem = (DocumentTypeModel)Pickdoctype.SelectedItem;
updatePickerValue = selectedItem.value;
}
catch (Exception ex)
{
}
}
// class
public class DocumentTypeModel
{
public string text { get; set; }
public string value { get; set; }
}
I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.
Is there some kind of additional Interface necessary to pass the action to the embedded button-component?
Any help is very appreciated since I am stuck at this issue for some days now
MyClass:
public class MyClass extends UIPanel implements SystemEventListener
{
private UIForm form;
private HtmlCommandButton buttonOk;
public MyClass()
{
FacesContext context = getFacesContext();
UIViewRoot root = context.getViewRoot();
root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}
#Override
public void processEvent(SystemEvent event)
{
this.form = new UIForm();
this.buttonOk = new HtmlCommandButton();
this.buttonOk.setId("okButtonId");
this.buttonOk.setActionExpression(getAction());
this.buttonOk.setValue("OK");
this.form.getChildren().add(this.buttonOk);
getChildren().add(this.form);
}
private enum PropertyKeys
{
action, text, titel
}
public MethodExpression getAction()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}
public void setAction(MethodExpression actionExpression)
{
getStateHelper().put(PropertyKeys.action, actionExpression);
}
public String getText()
{
return (String) getStateHelper().eval(PropertyKeys.text);
}
public void setText(String text)
{
getStateHelper().put(PropertyKeys.text, text);
}
public String getTitel()
{
return (String) getStateHelper().eval(PropertyKeys.titel);
}
public void setTitel(String titel)
{
getStateHelper().put(PropertyKeys.titel, titel);
}
#Override
public void encodeAll(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.DIV_ELEM, this);
writer.writeText(getText(), null);
this.form.encodeAll(context);
writer.endElement(HTML.DIV_ELEM);
}
#Override
public void encodeChildren(FacesContext context) throws IOException
{
}
#Override
public boolean isListenerForSource(Object source)
{
return (source instanceof MyClass);
}
}
MyClassHandler:
public class MyClassHandler extends ComponentHandler
{
public MyClassHandler(ComponentConfig config)
{
super(config);
}
#SuppressWarnings("rawtypes")
#Override
protected MetaRuleset createMetaRuleset(Class type)
{
return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}
}
myView Method:
...
public String myMethod()
{
System.err.println("myMethod");
return "/some/path/yadayada.xhtml";
}
...
MyView.xhtml
<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />
Exdending UICommand is enough, since you only want one action to be executed.
You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.
public void decode(FacesContext contex)
{
Map<String,String> map = context.getExternalContext.getRequestParameterMap();
// your rendered buttons need a name you check for
final boolean okPressed = map.containsKey( getClientId + ":ok" );
final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
if(okPressed || cancelPressed)
{
MethodExpression exp = null;
if(okPressed)
{
exp = getActionOk();
}
else
{
exp = getActionCancel();
}
// redirect to standard action
setActionExpression(exp);
queueEvent(new ActionEvent(this));
}
}
In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.
I want to update a log file(txt) everytime when methods in a an interface class are called?
Is there any way to do this other than writing code in every method to create log?
Here's my 30 mins. you'll have to implement the logging code somewhere so you have to create another abstraction for your code. thus an abstract class is needed. i think. this is very quick and dirty.
public interface IService<T>
{
List<T> GetAll();
bool Add(T obj);
}
then you'll need the abstract class where you'll need to implement your logging routine
public abstract class Service<T> : IService<T>
{
private void log()
{
/// TODO : do log routine here
}
public bool Add(T obj)
{
try
{
log();
return AddWithLogging(obj);
}
finally
{
log();
}
}
public List<T> GetAll()
{
try
{
log();
return GetAllWithLog();
}
finally
{
log();
}
}
protected abstract List<T> GetAllWithLog();
protected abstract bool AddWithLogging(T obj);
}
as for your concrete classes
public class EmployeeService : Service<Employee>
{
protected override List<Employee> GetAllWithLog()
{
return new List<Employee>() { new Employee() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Employee obj)
{
/// TODO : do add logic here
return true;
}
}
public class CompanyService : Service<Company>
{
protected override List<Company> GetAllWithLog()
{
return new List<Company>() { new Company() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Company obj)
{
/// TODO : do add logic here
return true;
}
}
public class Employee
{
public int Id {get;set;}
public string Name { get; set;}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
then on your implementation you can just..
static void Main(string[] args)
{
IService<Employee> employee = new EmployeeService();
List<Employee> employees = employee.GetAll();
foreach (var item in employees)
{
Console.WriteLine(item.Name);
}
IService<Company> company = new CompanyService();
List<Company> companies = company.GetAll();
foreach (var item in companies)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
hope this helps!
I think you would have to use Aspect Oriented Programming to achieve that. Read http://www.sharpcrafters.com/aop.net
I think you meant class (instead of interface)
Two options I can think of:
Implementing INotifyPropertyChanged which is in lines of writing code in every method
or
to adopt on of the AOP frameworks in the article http://www.codeproject.com/KB/cs/AOP_Frameworks_Rating.aspx if that is not a major leap