I've been trying to get recomposition to work but no luck... I tried many times and many approches - with no luck... can anyone point out my mistake? I expect that after I drop a new .dll into plugins directory the Senders collection will be automatically repopulated with new stuff...
//exported classes
[Export(typeof(ISender))]
public class SMTP : ISender
{
public string Name
{
get { return "SMTP plugin"; }
}
public void Send(string msg)
{
}
}
[Export(typeof(ISender))]
public class Exchange : ISender
{
public string Name
{
get { return "Exchange plugin"; }
}
public void Send(string msg)
{
// .. blah
}
}
/---------------------------------------------------------------------
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private const string STR_Pugins = ".\\plugins";
[ImportMany(typeof(ISender), AllowRecomposition = true)]
private List<ISender> Senders;
private DirectoryCatalog d;
CompositionContainer c;
public MainWindow()
{
InitializeComponent();
listBox1.DisplayMemberPath = "Name";
ConfigPlugins();
bindSenders();
}
private void ConfigPlugins()
{
DirectoryInfo dir = new DirectoryInfo(STR_Pugins);
if (!dir.Exists)
dir.Create();
d = new DirectoryCatalog(STR_Pugins);
d.Changed += new EventHandler<ComposablePartCatalogChangeEventArgs>(d_Changed);
c = new CompositionContainer(d);
c.ExportsChanged += new EventHandler<ExportsChangeEventArgs>(c_ExportsChanged);
c.ComposeParts(this);
}
void d_Changed(object sender, ComposablePartCatalogChangeEventArgs e)
{
bindSenders();
MessageBox.Show("d_Changed " + (Senders == null ? 0 : Senders.Count));
}
private void bindSenders()
{
listBox1.ItemsSource = Senders;
}
void c_ExportsChanged(object sender, ExportsChangeEventArgs e)
{
bindSenders();
MessageBox.Show("c_ExportsChanged "+ (Senders == null ? 0 : Senders.Count));
}
}
AFTER RESPONSE
ok, I've added the refresh, but still I don't get why the listbox won't populate with the new data...
public partial class MainWindow : Window
{
private const string STR_Pugins = ".\\plugins";
[ImportMany(typeof(ISender), AllowRecomposition = true)]
private List<ISender> Senders;
DirectoryCatalog d;
CompositionContainer c;
public MainWindow()
{
InitializeComponent();
listBox1.DisplayMemberPath = "Name";
ConfigPlugins();
bindSenders();
}
private void ConfigPlugins()
{
DirectoryInfo dir = new DirectoryInfo(STR_Pugins);
if (!dir.Exists)
dir.Create();
d = new DirectoryCatalog(STR_Pugins);
c = new CompositionContainer(d);
c.ComposeParts(this);
}
private void bindSenders()
{
label1.DataContext = Senders;
listBox1.ItemsSource = Senders;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
d.Refresh();
bindSenders();
}
}
You have to call Refresh yourself. If you want you can use a FileSystemWatcher object to get notified when the directory contents have changed.
It won't repopulate because when the field is updated, a brand new List is set to the field. The existing collection is not modified. You have to set it up as a property instead of a field (you can still make it protected or private), then when the "set" is called, you update the listBox1.ItemsSource and the label1.DataContext.
Related
I have two windows. In the first window I would like to start the second window with some preference values (e. g. "MaxWords"). The second window holds a class with an interface for INotifyPropertyChanged. This works as expected...
public partial class PreviewPreferences : Window
{
public PreviewPreferences()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel
{
public Preferences preferences { get; private set; }
public ViewModel()
{
preferences = new Preferences();
}
}
public class Preferences : INotifyPropertyChanged
{
private int _maxWords = 10;
/// <summary>
/// Default constructor
/// </summary>
public Preferences() { }
/// <summary>
/// Max words
/// </summary>
public int MaxWords
{
get { return this._maxWords; }
set { this._maxWords = value; this.OnPropertyChanged("MaxWords"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
The second window should updating the first window and adds some user controls to the grid. The question is not how to add the controls... it is more how to raise the event from the preference class that the value MaxWords is changed?
private void button_preview_preferences_Click(object sender, RoutedEventArgs e)
{
PreviewPreferences previewPreferences = new PreviewPreferences();
previewPreferences.Show();
Preferences preferences = new Preferences();
preferences.PropertyChanged += HandleChangedPreferences;
}
private void HandleChangedPreferences(object sender, PropertyChangedEventArgs e)
{
// this will never be raised
for (int i = 0; i < MaxWords; i++)
{
...
}
}
you have two instance of Preferences in button_preview_preferences_Click method. The first and important one (the one that changes) is hidden in PreviewPreferences DataContext:
private void button_preview_preferences_Click(object sender, RoutedEventArgs e)
{
var previewPreferences = new PreviewPreferences();
var preferences = (previewPreferences.DataContext as ViewModel).preferences;
preferences.PropertyChanged += HandleChangedPreferences;
previewPreferences.Show();
}
I suggest to invert the logic - create preferences outside ViewModel, and create ViewModel outside PreviewPreferences view:
public partial class PreviewPreferences : Window
{
public PreviewPreferences()
{
InitializeComponent();
}
}
public class ViewModel
{
public Preferences preferences { get; private set; }
public ViewModel(Preferences p)
{
preferences = p;
}
}
private void button_preview_preferences_Click(object sender, RoutedEventArgs e)
{
var preferences = new Preferences();
preferences.PropertyChanged += HandleChangedPreferences;
var previewPreferences = new PreviewPreferences();
previewPreferences.DataContext = new ViewModel(preferences);
previewPreferences.Show();
}
so what i would like to is setting back some attributes after an Custom Event is finished.
Scenario i have a save BackupDrives Class that does collection of data and then offers a Event to be called after its done.
Changing object properties can be done by button click, what i would like is to set them back to the same value after the event is finished.
Button click does the thing :
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.SaveDrives += OnSavingDrives;
backup.DrivesSave();
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
Now the triggered event method is not able to change the properties back.
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(....);
File.WriteAllLines(e.Something, e.List2ToSave);
File.WriteAllLines(e.Something_lse, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
How do i do this. Since this does not work.
And on other thig here to - when i run the GUI i need to click 2 times one the same button to start it all. Done Code Clean + Rebuild. Still the same.
---EDIT---
As for code here you go.
This is a Class for collecting method and event.
public class DrivesEventArgs : EventArgs
{
string MYDOC = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
const string backup_Drives = "....";
const string backup_Letters = "...";
public List<string> List1ToSave = new List<string>();
public List<string> List2ToSave = new List<string>();
public string SAVE_DRIVE_Letter
{
get
{
string name = Path.Combine(MYDOC, backup_Letters);
return name;
}
}
public string SAVE_DRIVE_Path
{
get
{
string name = Path.Combine(MYDOC, backup_Drives);
return name;
}
}
}
public class DrivesBackup
{
private const string path = "Network";
private List<string> drives_to_save = new List<string>();
private List<string> letters_for_drives = new List<string>();
private RegistryKey reg1, reg2;
public event EventHandler<DrivesEventArgs> SaveDrives;
public void DrivesSave()
{
var data = new DrivesEventArgs();
try
{
if (drives_to_save.Count == 0)
{
reg1 = Registry.CurrentUser.OpenSubKey(path);
string[] mounted_drives = reg1.GetSubKeyNames();
foreach (var drive in mounted_drives)
{ //get all UNC Paths from mounted_drives
string[] getUNC = { path, drive };
string fullUNC = Path.Combine(getUNC);
reg2 = Registry.CurrentUser.OpenSubKey(fullUNC);
string UNCPath = reg2.GetValue("RemotePath").ToString(); //getting UNC PATH
Console.WriteLine(UNCPath);
data.List1ToSave.Add(drive.ToString());
data.List2ToSave.Add(UNCPath);
OnSaveDrives(data);
}
}
}
catch (Exception er)
{
throw er;
}
}
protected virtual void OnSaveDrives(DrivesEventArgs eD)
{
SaveDrives?.Invoke(this, eD);
}
Now here is the MAINWINDOW WPF
public partial class MainWindow : Window
{
DrivesBackup backup = new DrivesBackup();
public MainWindow()
{
InitializeComponent();
}
private void bbdrives_Click(object sender, RoutedEventArgs e)
{
backup.DrivesSave();
backup.SaveDrives += OnSavingDrives;
Drive_text.Visibility = Visibility.Visible;
drives_progres.Visibility = Visibility.Visible;
drives_progres.IsIndeterminate = true;
}
private void OnSavingDrives(object sender, DrivesEventArgs e)
{
Directory.CreateDirectory(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), #"SEG-Backup"));
File.WriteAllLines(e.SAVE_DRIVE_Path, e.List2ToSave);
File.WriteAllLines(e.SAVE_DRIVE_Letter, e.List1ToSave);
Drive_text.Visibility = Visibility.Collapsed;
drives_progres.Visibility = Visibility.Collapsed;
drives_progres.IsIndeterminate = false;
}
}
Now i do hope this would make some things more clear.
Currently in my program a user opens form 1 to create a new instance of a class and it is then saved to a list. After form 1 closes I would like the main form to reload and show the updated list on the screen. I am having trouble figuring out how to refresh the main navigation and how I would get the list to show on the form.
MainNaviagation
public partial class MainNavigation : Form
{
private Model m_modelObj;
public MainNavigation(Model modelObj)
{
InitializeComponent();
m_modelObj = modelObj;
m_modelObj.ChocolateAdded += m_modelObj_ChocolateAdded;
}
void m_modelObj_ChocolateAdded(Chocolate newChocolate)
{
//whole list of chocolates
List<Chocolate> chocolateList = m_modelObj.ChocolateList;
}
private void button1_Click(object sender, EventArgs e)
{
string candy = comboBox1.SelectedItem.ToString();
Form1 aForm1 = new Form1(textBox1.Text, candy, m_modelObj);
aForm1.ShowDialog();
}
}
Model Class:
{
public delegate void ChocolateAddedEventHander(Chocolate newChocolate);
public class Model
{
public event ChocolateAddedEventHander ChocolateAdded;
public List<Chocolate> ChocolateList = new List<Chocolate>();
public void AddChocolateInList(Chocolate chocolate)
{
ChocolateList.Add(chocolate);
if (ChocolateAdded != null)
ChocolateAdded(chocolate);
}
}
form1
public partial class Form1 : Form
{
Model m_model;
public Form1(string name, string candy, Model modelObj)
{
InitializeComponent();
m_model = modelObj;
string str = name + " selected : ";
label1.Text = str;
}
private void button1_Click(object sender, EventArgs e)
{
Chocolate newChocolate = new Chocolate(comboBoxChocolateSelection.SelectedItem.ToString(), 12.5, true, 2);
m_model.AddChocolateInList(newChocolate);
this.Close();
}
}
chocolates
public class Chocolate
{
#region Fields
public string flavor;
public double cost;
public bool giftWrap;
public int quantity;
#endregion End of Fields
#region Constructors
public Chocolate(string flavor, double cost, bool giftWrap, int quantity)
{
this.flavor = flavor;
this.cost = cost;
this.giftWrap = giftWrap;
this.quantity = quantity;
}
#endregion End of Constructors
}
Here is the Main.xaml.cs page details,
private void Btn_Ok_Click(object sender, RoutedEventArgs e)
{
String homeTeamId = TeamIdtxt.Text;
this.DataContext = new MainViewModel();
}
and my class1.cs will be like as follows,
public MainViewModel()
{
Players = new ObservableCollection<PlayersViewModel>();
string url = "http://192.168.1.19/projects/t20lite/index.php/api/api/get_playersbyteam";
var task = new HttpGetTask<PlayerList>(url, this.OnPostExecute);
task.OnPreExecute = this.OnPreExecute;
task.OnError = this.OnError;
task.Execute();
}
how i have to pass the hometeam id value to mainviewmodel, In there i have to append it with url.
As sugested in comments you can share data by several options:
Option 1: Using Data Binding
http://msdn.microsoft.com/en-us/library/ms752347(v=vs.110).aspx
Option 2: Define property in your ViewModel class to pass it. Add some method to handle get player request in your view-model. For example:
public class MainViewModel
{
public string TeamID { get; set; }
public MainViewModel()
{
Players = new ObservableCollection<PlayersViewModel>();
}
public void GetPlayer()
{
string url = "http://192.168.1.19/projects/t20lite/index.php/api/api/get_playersbyteam;"
// Do something with url and tour TeamID
var task = new HttpGetTask<PlayerList>(url, this.OnPostExecute);
task.OnPreExecute = this.OnPreExecute;
task.OnError = this.OnError;
task.Execute();
}
}
And you need to create your ViewModel once. So I suggest you to create your ViewModel in constructor of your View instead of button-click handler.
public class MainView
{
public MainView()
{
InitializeComponent();
this.ViewModel = new MainViewModel();
}
public MainViewModel ViewModel
{
get { return this.DataContext as MainViewModel; }
set { this.DataContext = value; }
}
private void TeadIdText_TextChanged(object sender, TextChangedEventArgs e)
{
this.ViewModel.TeamID = TeamIdtxt.Text;
}
private void Btn_Ok_Click(object sender, RoutedEventArgs e)
{
this.ViewModel.GetPlayer();
}
}
Well I was trying to assign global variable to read values from Check boxes and radio buttons but the values don't update when the selections are changed ! Where have I done wrong? Here's the code:
private void chkInMut_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.Mutate = 1;
}
private void chkShwCal_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.ShowCal = 1;
}
private void chkOutSol_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.OutCal = 1;
}
}
public static class GlobalVar
{
static int _MaxMin, _MutVal, _CalShow, _CalOut;
/// <summary>
/// Access routine for global variable.
/// </summary>
public static int Extrema
{
get
{
return _MaxMin;
}
set
{
_MaxMin = value;
}
}
public static int Mutate
{
get
{
return _MutVal;
}
set
{
_MutVal = value;
}
}
public static int ShowCal
{
get
{
return _CalShow;
}
set
{
_CalShow = value;
}
}
public static int OutCal
{
get
{
return _CalOut;
}
set
{
_CalOut = value;
}
}
}
when I try to print the numbers using this test satement, the values returned are unexpected :
maxMin = GlobalVar.Extrema;
calShow = GlobalVar.ShowCal;
calOut = GlobalVar.OutCal;
IsMutble = GlobalVar.Mutate;
txtOutput.Text += Convert.ToString("\nMaxima Minima"+maxMin+"\n"+"Show Cal : "+calShow+"\n"+"Output Cal :"+calOut+"\n"+"Mutate : "+IsMutble+"\n---------\n");
And when I check/un-check the boxes, the values are not updated as it should be. Where have I gone wrong?
Edit: Solved by adding Unchecked Parameter.
Probably you should write your event handlers like this
private void chkInMut_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.Mutate = (chkInMut.IsChecked ? 1 : 0);
}
and so on .....
I think the problem is with your public static properties. for example try this:
public static int Extrema
{
get
{
return GlobalVar._MaxMin;
}
set
{
GlobalVar._MaxMin = value;
}
}
and do the same for all other properties.
Edit:
and why are you using this stucture? You can set your static class to be like this:
public static class GlobalVar
{
public static int Extrema;
public static int Mutate;
public static int ShowCal;
}