I want to call UpdataDataSet() from a menu, but I don't know how to reference the non-static member below. The menu item gets created, but it fails to call the UpdateDataSet() method.
The entire project code is given below:
CODE:
private void mniClick(object sender, RoutedEventArgs e) //menu click handler { screenerDataSetProvider.UpdateDataSet(); }
The entire project code is given below:
CODE:
using System.Windows.Controls; using System.Windows.Media; using System.Windows; using WealthLab.Core; using WealthLab.Data; /* DataSetProviderBase class here */ using WealthLab.WPF; namespace ScreenerDataSets { public class ImportScreenerHits : WL8ClientExtensionBase { ScreenerDataSetProvider screenerDataSetProvider = new ScreenerDataSetProvider(); public override string Name => "Import Screener Hits"; //extension name public override List<MenuItem> GetMenuItems() //return extension menu item { List<MenuItem> mi = new List<MenuItem>(); MenuItem mni = CreateExtensionMenuItem("Import stocks", GetGlyph(), mniClick); mi.Add(mni); return mi; } private void mniClick(object sender, RoutedEventArgs e) //menu click handler { screenerDataSetProvider.UpdateDataSet(); } //Use the WealthLab.WPF GlyphManager to get an ImageSource from our embedded resource private ImageSource GetGlyph() { return GlyphManager.GetImageSource("ScreenerDataSets.stkImport.png", this); } } public class ScreenerDataSetProvider : DataSetProviderBase //www.wealth-lab.com/Discussion/Update-DataSets-property-in-DataSetProvider-while-WL-is-running-8173 { //public override string Name => "ScreenerDataSets"; List<DataSet> dataSets = new List<DataSet>(); DataSet screenerStocks = new DataSet(); // { Name = "stocks" }; public override void Initialize() { List<string> stockList = new List<string> { "AAPL", "MSFT", "TSLA" }; screenerStocks.Symbols.AddRange(stockList); //add symbols to dataSet screenerStocks.Name = "stocks"; screenerStocks.PreferredDataProviderName = "IQFeed"; dataSets.Add(screenerStocks); //add dataSet to List<DataSet> base.Initialize(); } public override List<DataSet> DataSets => this.dataSets; public void UpdateDataSet() { List<string> updatedHighVolatilityStocks = new List<string> { "GME", "COST", "PINS" }; var updatedVolatileDataSet = new DataSet() { Name = "Volatile", Symbols = updatedHighVolatilityStocks }; //remove old DataSet from DataSets? dataSets.RemoveAll(x => x.Name == screenerStocks.Name); //add updated DataSet to DataSets dataSets.Add(updatedVolatileDataSet); } } }
Rename
You need to hook up the menu item’s OnClick handler.
QUOTE:
You need to hook up the menu item’s OnClick handler.
I don't understand. I thought I did that below. What's missing? For this answer, let's assume I know nothing about WPF.
CODE:
private void mniClick(object sender, RoutedEventArgs e) //menu click handler { screenerDataSetProvider.UpdateDataSet(); }
Sorry yes, I was not at the keyboard before. I think you have it correct. For reference, there's the complete code for the Candlestick Genetic Evolver reference. Try putting some code like a MessageBox.Show in the menu event handler to verify it's getting called, it should be!
CODE:
using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using WealthLab.Core; using WealthLab.WPF; namespace WealthLab.Candlesticks { //Candlesticks Extension object public class CandlesticksClientExtension : WL8ClientExtensionBase { //Name public override string Name => "Candlesticks"; //initialize public override List<MenuItem> GetMenuItems() { List<MenuItem> mi = new List<MenuItem>(); MenuItem mni = CreateExtensionMenuItem("Candlestick Genetic Evolver", Glyph, mniClick); mi.Add(mni); return mi; } //get the child window from a workspace token public override ChildWindow GetChildWindow(string token) { if (token != "Candlesticks") return null; return CreateChildWindow(); } //process help action token public override bool ProcessHelpToken(string token) { if (token == "Candlesticks") { CreateChildWindow(); return true; } return false; } //get the Preference Page public override List<TabPage> PreferencePages { get { List<TabPage> lst = new List<TabPage>(); lst.Add(new prefCandlesticks()); return lst; } } //private members //click handler for generated menu item private void mniClick(object sender, RoutedEventArgs e) { CreateChildWindow(); } //create the child window private ChildWindow CreateChildWindow() { cwCandlesticks cw = new cwCandlesticks(); MyClientHost.ShowExtensionChildWindow(cw, "Candlestick Genetic Evolver", Glyph); return cw; } //return the Glyph private ImageSource Glyph => GlyphManager.GetImageSource("WealthLab.Candlesticks.WPF.Glyphs.Candlesticks.png", this); } }
QUOTE:
here's the complete code for the Candlestick Genetic Evolver reference.
That's exactly the code example I used to write my original attempt.
QUOTE:
Try putting some code like a MessageBox.Show in the menu event handler to verify it's getting called, it should be!
That's an excellent suggestion. If you "think" the UpdateDataSet() method is the culprit, do you see anything in that method that would cause it to fail?
Notice I'm populating List<DataSet> with the "new" operator in a field variable declaration instead of within Initialize(). Is that allowed? ... Or is the "new" operator a run-time statement that can only be used within ScreenerDataSetProvider.Initialize?
You are correct. The MessageBox.Show dialog says the menu item is working correctly.
It's the UpdateDataSet() method that's failing. It's now throwing an error against the dataSets object, which should be visible throughout the entire ScreenerDataSetProvider class, but it's not. Please tell me why not?
It's the UpdateDataSet() method that's failing. It's now throwing an error against the dataSets object, which should be visible throughout the entire ScreenerDataSetProvider class, but it's not. Please tell me why not?
It looks to me like the datasets variable is declared, but you're not creating an instance of it. The new operator line is commented out.
No, I moved the "new" operator inside Initialize thinking that might fix the problem, but it doesn't. Initialize does successfully execute and create the "stocks" dataset on WL startup, but that maybe part of the problem. Read at the bottom.
Alternatively, if there's a way to suppress the ScreenerDataSetProvider instance created by WL proper on startup, then ImportScreenerHits could create its own "private" instance instead so the CLR won't be confused by two separate ScreenerDataSetProvider instances. I suppose you could add a Boolean parameter to DataSetProviderBase to tell it not to create a ScreenerDataSetProvider instance on WL startup. That seems like a confusing solution, but so does the first method in the paragraph above. What's the cleanest solution?
CODE:The problem is WL proper creates an instance of ScreenerDataSetProvider, but then ImportScreenerHits creates a second instance and references that one. Keep reading below.
public class ScreenerDataSetProvider : DataSetProviderBase //www.wealth-lab.com/Discussion/Update-DataSets-property-in-DataSetProvider-while-WL-is-running-8173 { //public override string Name => "ScreenerDataSets"; List<DataSet> dataSets;// = new List<DataSet>(); DataSet screenerStocks = new DataSet(); // { Name = "stocks" }; public override void Initialize() { dataSets = new List<DataSet>(); List<string> stockList = new List<string> { "AAPL", "MSFT", "TSLA" }; screenerStocks.Symbols.AddRange(stockList); //add symbols to dataSet screenerStocks.Name = "stocks"; screenerStocks.PreferredDataProviderName = "IQFeed"; dataSets.Add(screenerStocks); //add dataSet to List<DataSet> base.Initialize(); } public override List<DataSet> DataSets => dataSets;
CODE:What ImportScreenerHits needs to do is issue a CLR method to locate the original instance created by WL proper and use its reference within its own code (so there's only one ScreenerDataSetProvider instance in play). Do you know how I can do that?
public class ImportScreenerHits : WL8ClientExtensionBase { ScreenerDataSetProvider screenerDataSetProvider = new ScreenerDataSetProvider(); public override string Name => "Import Screener Hits"; //extension name
Alternatively, if there's a way to suppress the ScreenerDataSetProvider instance created by WL proper on startup, then ImportScreenerHits could create its own "private" instance instead so the CLR won't be confused by two separate ScreenerDataSetProvider instances. I suppose you could add a Boolean parameter to DataSetProviderBase to tell it not to create a ScreenerDataSetProvider instance on WL startup. That seems like a confusing solution, but so does the first method in the paragraph above. What's the cleanest solution?
You should never create your own instance of the Provider. WL8 creates the instances using its internal factories.
QUOTE:
WL8 creates the instances using its internal factories.
So how does my menu extension find the instance WL8 created? Is there an example given somewhere?
You want your menu event handler to find the DataSetProvider instance?
One way would be to use the Singleton pattern, create a static instance in your class and assign the value in the constructor.
Another way would be to use the DataSetProviderFactory.Instance.Find to find the instance of your Provider by "Name."
One way would be to use the Singleton pattern, create a static instance in your class and assign the value in the constructor.
Another way would be to use the DataSetProviderFactory.Instance.Find to find the instance of your Provider by "Name."
And where are both of these methods documented?
I attempted to implemented your Post #10 suggestion and failed. I think I'm way over my head here. I'm inclined to revert to my old WL6 command-line implementation that works--which is unfortunate. I did submit a concierge request to get this done with a DataSetProvider approach, which I could learn from, but you haven't responded.
And I think the documentation for practical use of the DataSetProvider paradigm is totally inadequate. I'm just stating the truth here--nothing personal.
If you can fix the problem below, that would help.
And I think the documentation for practical use of the DataSetProvider paradigm is totally inadequate. I'm just stating the truth here--nothing personal.
If you can fix the problem below, that would help.
datasetProviderBase.UpdateDataset();
But you’ll also need to define dataSetProviderBase as an instance of your class instead of the base class. Cast the result of the Find to your class.
But you’ll also need to define dataSetProviderBase as an instance of your class instead of the base class. Cast the result of the Find to your class.
What's the point of all this? To save one click from doing it in the Data Manager instead?
QUOTE:
What's the point of all this?
It's to import the Excel results created by the Fidelity stock screener. In WL6, I use a special command-line app to do that with. It removes screener candidates in existing WL datasets, then creates a new WL dataset for just the new and unique candidates for WL6 to evaluate further. The command-line app works with any program (stat package, Firefox, WL6, MatLab), which is nice.
The only real advantage of using the DataSetProvider approach is so I don't have to restart WL to see the new datasets.
I guess you're wondering if I shouldn't just stay with the command-line app approach? I'm wondering that too.
@superticker - the code below should help. It'll update the dataset when you click the extension menu item. However, the update won't show in the Data Manager if you already have the "stocks" dataset showing. If you click away from the "stocks" dataset and then click on it again then the updated list appears. Perhaps there is a programmatic way to update the user interface, but I couldn't figure that out. Maybe Glitch can help.
Also, I changed the Glyph, so you may want to set it to what you had. And, I changed the PreferredDataProviderName, so you may want to set it back to IQFeed.
I'm not sure about the overall order of operations you want to suit your requirements, but this is a start...
Also, I changed the Glyph, so you may want to set it to what you had. And, I changed the PreferredDataProviderName, so you may want to set it back to IQFeed.
I'm not sure about the overall order of operations you want to suit your requirements, but this is a start...
CODE:
using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using WealthLab.Core; using WealthLab.Data; using WealthLab.WPF; namespace ScreenerDataSets { public class ImportScreenerHits : WL8ClientExtensionBase { private readonly ScreenerDataSetProvider dataSetProvider = (ScreenerDataSetProvider) DataSetProviderFactory.Instance.Find(ScreenerDataSetProvider.ProviderName); public override string Name => "Import Screener Hits"; //extension name public override List<MenuItem> GetMenuItems() //return extension menu item { var mi = new List<MenuItem>(); var mni = CreateExtensionMenuItem("Import stocks", GetGlyph(), mniClick); mi.Add(mni); return mi; } private void mniClick(object sender, RoutedEventArgs e) //menu click handler { dataSetProvider.UpdateDataSet(); } //Use the WealthLab.WPF GlyphManager to get an ImageSource from our embedded resource private ImageSource GetGlyph() => GlyphManager.GetImageSource("WealthLab.Candlesticks.WPF.Glyphs.Candlesticks.png", this); // "ScreenerDataSets.stkImport.png" } public class ScreenerDataSetProvider : DataSetProviderBase { public const string ProviderName = "ScreenerDataSets"; private const string ScreenerDataSetName = "stocks"; private readonly List<DataSet> dataSets = []; private readonly DataSet screenerStocks = new(); // { Name = "stocks" }; public override string Name => ProviderName; public override List<DataSet> DataSets => dataSets; public override void Initialize() { base.Initialize(); var stockList = new List<string> {"AAPL", "MSFT", "TSLA"}; screenerStocks.Symbols.AddRange(stockList); //add symbols to dataSet screenerStocks.Name = ScreenerDataSetName; screenerStocks.PreferredDataProviderName = "Schwab"; // "IQFeed"; dataSets.Add(screenerStocks); } public void UpdateDataSet() { var updatedHighVolatilityStocks = new List<string> {"GME", "COST", "PINS"}; var ds = dataSets.Find(t => t.Name == ScreenerDataSetName); ds.Symbols.Clear(); ds.Symbols.AddRange(updatedHighVolatilityStocks); // force "Need to Load Data" in any open strategies DataSetFactory.Instance.UpdateSymbols(ds, updatedHighVolatilityStocks); } } }
I made changes and removed all "extra" code, but I still cannot get the abbreviated code below to work. I'm wondering if there's a problem with WL itself that's not letting it work. But if someone can post a working example, that would be great.
Another option would be to let my command-line app read, evaluate, and create the new datasets (which may be better), then have an extension menu item that would let the new datasets appear without restarting WL8. That would be really nice.
Another option would be to let my command-line app read, evaluate, and create the new datasets (which may be better), then have an extension menu item that would let the new datasets appear without restarting WL8. That would be really nice.
CODE:
using System.Collections.Generic; using System.Windows.Controls; using System.Windows.Media; using System.Windows; using WealthLab.Core; using WealthLab.Data; /* DataSetProviderBase class here */ using WealthLab.WPF; /* WL8ClientExtensionBase class here */ namespace ScreenerDataSets { public class ImportScreenerHits : WL8ClientExtensionBase { ScreenerDataSetProvider screenerDataSetProvider = (ScreenerDataSetProvider)DataSetProviderFactory.Instance.Find("ScreenerDataSets"); public override string Name => "Import Screener Hits"; //extension name public override List<MenuItem> GetMenuItems() //return extension menu item { List<MenuItem> mi = new List<MenuItem>(); MenuItem mni = CreateExtensionMenuItem("Import screener stocks", GetGlyph(), mniClick); mi.Add(mni); return mi; } private void mniClick(object sender, RoutedEventArgs e) //menu click handler { screenerDataSetProvider.UpdateDataSet(); MessageBoxResult result = MessageBox.Show("Hello world"); } //Use the WealthLab.WPF GlyphManager to get an ImageSource from our embedded resource private ImageSource GetGlyph() { return GlyphManager.GetImageSource("ScreenerDataSets.stkImport.png", this); } } public class ScreenerDataSetProvider : DataSetProviderBase //www.wealth-lab.com/Discussion/Update-DataSets-property-in-DataSetProvider-while-WL-is-running-8173 { public override string Name => "ScreenerDataSets"; List<DataSet> dataSets = new List<DataSet>(); DataSet screenerStocks = new DataSet(); // { Name = "stocks" }; public override void Initialize() { /* List<string> stockList = new List<string> { "AAPL", "MSFT", "TSLA" }; screenerStocks.Symbols.AddRange(stockList); //add symbols to dataSet screenerStocks.Name = "stocks"; screenerStocks.PreferredDataProviderName = "IQFeed"; dataSets.Add(screenerStocks); //add dataSet to List<DataSet> */ base.Initialize(); } public override List<DataSet> DataSets => dataSets; public void UpdateDataSet() { List<string> updatedHighVolatilityStocks = new List<string> { "GME", "COST", "PINS" }; /*DataSet updatedVolatileDataSet = new DataSet() { Name = "Volatile" }; */ screenerStocks.Name = "stocks"; screenerStocks.PreferredDataProviderName = "IQFeed"; screenerStocks.Symbols.AddRange(updatedHighVolatilityStocks); //remove old DataSet from DataSets? //dataSets.RemoveAll(x => x.Name == screenerStocks.Name); //add updated DataSet to DataSets dataSets.Add(screenerStocks); } } }
I slightly modified Paul's code to change the symbols every time you click the menu. If you run this you'll see the symbol change in the DataSet tree on every click. So, at this point it proves that it's working and I don't know why you think it's not.

CODE:
using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using WealthLab.Core; using WealthLab.Data; using WealthLab.WPF; namespace ScreenerDataSets { public class ImportScreenerHits : WL8ClientExtensionBase { private readonly ScreenerDataSetProvider dataSetProvider = (ScreenerDataSetProvider)DataSetProviderFactory.Instance.Find(ScreenerDataSetProvider.ProviderName); public override string Name => "Import Screener Hits"; //extension name public override List<MenuItem> GetMenuItems() //return extension menu item { var mi = new List<MenuItem>(); var mni = CreateExtensionMenuItem("Import stocks", GetGlyph(), mniClick); mi.Add(mni); return mi; } private void mniClick(object sender, RoutedEventArgs e) //menu click handler { dataSetProvider.UpdateDataSet(); } //Use the WealthLab.WPF GlyphManager to get an ImageSource from our embedded resource private ImageSource GetGlyph() => GlyphManager.GetImageSource("WealthLab.Candlesticks.WPF.Glyphs.Candlesticks.png", this); // "ScreenerDataSets.stkImport.png" } public class ScreenerDataSetProvider : DataSetProviderBase { public const string ProviderName = "ScreenerDataSets"; private const string ScreenerDataSetName = "stocks"; private readonly List<DataSet> dataSets = []; private readonly DataSet screenerStocks = new(); // { Name = "stocks" }; public override string Name => ProviderName; public override List<DataSet> DataSets => dataSets; private static int counter = 0; public override void Initialize() { base.Initialize(); var stockList = new List<string> { "SYM" + counter++, "SYM" + counter++, "SYM" + counter++ }; screenerStocks.Symbols.AddRange(stockList); //add symbols to dataSet screenerStocks.Name = ScreenerDataSetName; //screenerStocks.PreferredDataProviderName = "Schwab"; // "IQFeed"; dataSets.Add(screenerStocks); } public void UpdateDataSet() { var updatedHighVolatilityStocks = new List<string> { "SYM" + counter++, "SYM" + counter++, "SYM" + counter++ }; var ds = dataSets.Find(t => t.Name == ScreenerDataSetName); ds.Symbols.Clear(); ds.Symbols.AddRange(updatedHighVolatilityStocks); // force "Need to Load Data" in any open strategies DataSetFactory.Instance.UpdateSymbols(ds, updatedHighVolatilityStocks); } } }
I can confirm the above code in Post #18 works.
So the code below is required to reread the datasets? I didn't know that. Is that documented anywhere?
So the code below is required to reread the datasets? I didn't know that. Is that documented anywhere?
CODE:What's the base directory VS uses to find the glyph in the line below? What I'm doing now isn't working.
// force "Need to Load Data" in any open strategies DataSetFactory.Instance.UpdateSymbols(ds, updatedHighVolatilityStocks);
CODE:
ImageSource GetGlyph() => GlyphManager.GetImageSource("ScreenerDataSets.stkImport.png", this);
There are a ton of things the WL8 framework can do that I haven’t documented. One of them is dynamically updating a DataSet’s symbols. I don’t have enough years left in my life to document it all. The best I can do is help explain the undocumented on an as needed basis.
QUOTE:
I don’t have enough years left in my life to document it all.
I hope you live a very long life just in case another question comes up. :)
The GlyphManager can't find my glyph. How should I specify the directory it's in? See Post #19.
ImageSource, the return type of GetGlyph(), is a class that exists in namespace System.Windows.Media. It has a ton of subclasses. You can just use the the Uri and BitmapImage classes to load your png as shown below. In the code, just replace the path to your png file.
Note: In WL8, in Assembly References (use menu WL8 menu item Tools -> Assembly References) DO NOT have System.Private.Uri checked or there will be namespace collisions.
Note: In WL8, in Assembly References (use menu WL8 menu item Tools -> Assembly References) DO NOT have System.Private.Uri checked or there will be namespace collisions.
CODE:
private ImageSource GetGlyph() { var uri = new System.Uri(@"C:\Windows\SystemResources\Windows.UI.Shell\Images\PasswordExpiry.scale-400.png"); return new BitmapImage(uri); }
Bypassing the GlyphManager as shown below (Post #22) causes WL8 to hang on startup.
CODE:
/* ImageSource GetGlyph() => GlyphManager.GetImageSource("ScreenerDataSets.Glyphs.stkImport.png", this); */ private ImageSource GetGlyph() { var uri = new System.Uri(@"ScreenerDataSets.Glyphs.stkImport.png"); return new BitmapImage(uri); }
Just follow the example, and make sure your png is saved as an Embedded Resource in Visual Studio.
Scratch this post. Instead see Glitch's response in Post #24.
QUOTE:
saved as an Embedded Resource in Visual Studio.
So this Embedded Resource business is a fancy way to save an itsy bitsy file (e.g. glyph) as a library member into a DLL so it doesn't get fragmented and take up so much disk space. I didn't know DLLs could be made into non-code (resource) libraries via Visual Studio.
For those as dumbfounded as me about Visual Studio's librarian capabilities, you can read about them here. https://stackoverflow.com/questions/39367349/code-or-command-to-use-embedded-resource-in-visual-studio
Anyway, following Post #24, I got my extension glyph working. This could have been documented better in WealthLabClientExtension. But thanks for all the support. It's been "fun".
Your Response
Post
Edit Post
Login is required