I'm writing a custom Excel add-in that assigns specific keyboard shortcuts to tasks such as number formatting for selected cells only. When I try to add a shortcut that will recalculate the selected range only, the entire sheet is re-calculated.
public class Main : IExcelAddIn
{
public void AutoOpen()
{
dynamic app = ExcelDnaUtil.Application;
app.OnKey("^N", "FormatNumbers");
app.OnKey("^C", "CalcSelection");
}
public void AutoClose()
{
}
}
public class KeyboardShortcuts
{
public static void FormatNumbers()
{
dynamic app = ExcelDnaUtil.Application;
dynamic selection = app.Selection;
selection.NumberFormat = "#,##0;[Red]-#,##0";
}
public static void CalcSelection()
{
dynamic app = ExcelDnaUtil.Application;
dynamic selection = app.Selection;
selection.Calculate();
}
}
The FormatNumbers method works perfectly but the CalcSelection method forces the entire sheet to be recalculated rather than just the bits I've selected.
Would appreciate any suggestions to make this code work?
There are various quirks related to the Excel Range.Calculate call. You can find some good information on the FastExcel site from Charles Williams: https://www.decisionmodels.com/calcsecretsg.htm
If the book is in automatic calculation mode, it sounds like Range.Calculate will recalculate all volatile cells on all worksheets.
Related
This question already has answers here:
How to load image to WPF in runtime?
(2 answers)
Closed 1 year ago.
I have a custom control with a default image that I want to change based on which iteration of the control it is. For example, I have one for "F1" and "NumLock" and so on. In the constructor of the control, I have this:
public FixerBox(Dictionary<string,string> deets)
{
InitializeComponent();
btnOff();
this.FixerTitle.Text = deets["Title"];
this.FixerDesc.Text = deets["Description"];
this.FixerTags.Text = deets["Tags"];
this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
}
The bitmap stuff was based on another answer and produces this:
Below is the control itself showing that it's correctly getting the title, tags, and description, but the image is bunk (on the left side, that thin grey line is the border that should be around the image).c#
If I was using HTML/CSS, I could right-click the image to see what exactly its properties are, but I don't know how to get that kind of information using WPF. The best I could manage was in the top area is a status window where I've manually printed a "Tostring" output of the first controls image source data. Near as I can tell, it's all correct, but there's no actual image there. Every subsequent control has the same output (one thin line where the image should be).
EDIT Per comments, here is some more of the information. The main XAML file loads up the controls like so in its constructor:
public partial class MainWindow : Window
{
private Fixers fixers = new Fixers();
// This is the custom control consisting mostly of various boxes
private Dictionary<string,FixerBox> fixerBoxes = new Dictionary<string, FixerBox> { };
public MainWindow()
{
InitializeComponent();
var fixNames = fixers.FixerNames();
foreach (string key in fixNames)
{
fixerBoxes[key] = new FixerBox(fixers.GetFix(key));
FixersArea.Children.Add(fixerBoxes[key]);
}
StatusBox.Text += fixerBoxes["F1"].FixerImg.Source.ToString();
}
}
The fixers variable is of class Fixers which consists of the below (abbreviated to show just the F1 function for brevity):
class Fixers
{
private string ClearWS(string str)
{
var first = str.Replace(System.Environment.NewLine, "");
return first.Replace("\t", "");
}
// Loads registry functions
private Regis regStuff = new Regis();
// Loads preferences from the file
private Prefs prefs = new Prefs();
// A timer to make sure the system behaves
private Timer watcher;
// Watcher action toggles
private bool watchNumL = false;
// Translation array from fix shortname to various data about them
private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
{
["F1"] = new Dictionary<string,string> {
["PrefName"] = "KillF1UnhelpfulHelp",
["Img"] = #"/graphics/F1key.png",
["Title"] = #"Diable F1 ""Help"" function",
["Description"] = #"
Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result?
Windows set the F1 key to a generic help function that basically never helps and always gets in the way.
Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord,
but this will stop the default Windows behavior in things like Windows Explorer at least.
",
["Tags"] = "#Keyboard,#Rage"
},
};
public Fixers()
{
// The readability hack above with multi-line strings introduces a bunch of extra whitespace. Let's clear that out
foreach (var fixKey in fixers.Keys)
{
fixers[fixKey]["Description"] = ClearWS(fixers[fixKey]["Description"]);
}
}
public List<string> FixerNames()
{
return fixers.Keys.ToList();
}
public bool IsFixed(string which)
{
// If we're watching, it's fixed
if ("NumL" == which) return watchNumL;
// For anything registry related
return regStuff.IsFixed(which);
}
public Dictionary<string,string> GetFix(string which)
{
return fixers[which];
}
}
if you use binding, you can create in your ViewModel a string, in which is stored the path of your image, then you can easily change programatically its path.
Then in XAML just bind image's source to the string.
In my case I have a list of objects, with the property `ImageName' :
<Image Source="{Binding DataContext.SelectedMacro.ImageName,
RelativeSource={RelativeSource AncestorType=Window}}"/>
I have a class Comparer, which defines the following:
// partial Comparer code
public class Comparer
{
private readonly Color colorWarning = Color.Red;
private readonly string SPREADSHEET_RED_WARNING_STYLE = "red warning style";
private OfficeOpenXml.Style.XmlAccess.ExcelNamedStyle redWarningStyle;
}
This class has a method prepareSpreadsheet:
private void prepareSpreadsheet()
{
// spreadsheet styles
redWarningStyle = spreadsheet.Workbook.Styles.CreateNamedStyle(SPREADSHEET_RED_WARNING_STYLE);
redWarningStyle.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
redWarningStyle.Style.Fill.BackgroundColor.SetColor(colorWarning);
redWarningStyle.Style.Font.Color.SetColor(Color.Black)
}
If the spreadsheet file already contains such a named style, an exception is thrown. Can Epplus programatically check if a certain named style already exists in the spreadsheet, and remove it if it does?
I have managed to get it working, but not sure if it is the best solution. It does not remove them, only adds if it finds a style with an existing name (cannot guarantee it has the same style):
// retrieve a list of styles from the spreadsheet
List<OfficeOpenXml.Style.XmlAccess.ExcelNamedStyleXml> spreadsheetNamedStyles = spreadsheet.Workbook.Styles.NamedStyles.ToList();
// check if it already exists before attempting to add it
if (spreadsheetNamedStyles.FirstOrDefault(namedStyle => namedStyle.Name.Equals(SPREADSHEET_RED_WARNING_STYLE)) == null)
{
redWarningStyle = spreadsheet.Workbook.Styles.CreateNamedStyle(SPREADSHEET_RED_WARNING_STYLE);
redWarningStyle.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
redWarningStyle.Style.Fill.BackgroundColor.SetColor(colorWarning);
redWarningStyle.Style.Font.Color.SetColor(Color.Black);
}
Okay, so I'm doing my first assessment/task in my programming course, so keep in mind I'm very new to this.
So I've got a CSV file with a list of products with stock amounts, name, code and OnOrder properties, and I've done what I think is the first big step of writing the program to take said list and put it in the program into a DataGridView (this is all freeform but I have to firstly use a list or array, and secondly must have sorting by either item code or count etc).
here's my code so far:
namespace SkillageAppForm
{
class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
args = new[] { "C:\\StockFile\\stocklist.csv" };
var dailyvalues = ReadValues(args[0]);
}
public class DailyValues
{
public string ItemCode;
public string ItemDescription;
public int CurrentCount;
public string OnOrder;
public static DailyValues FromLine(string line)
{
var data = line.Split(',');
return new DailyValues()
{
ItemCode = data[0],
ItemDescription = data[1],
CurrentCount = int.Parse(data[2]),
OnOrder = data[3]
};
}
}
public IList<DailyValues> ReadValues(string path)
{
var myList = new List<DailyValues>();
foreach (var line in File.ReadLines(path).Skip(1))
{
myList.Add(DailyValues.FromLine(line));
}
return myList;
}
}
so, running this and setting a break point, it seems to populate myList just like I want it to, so yay. But now I'm super confused as to how to get that list into the DataGridView, the way I've tried so far is just by using a click event to load it into the DataGridView using datasource (I'm assuming therefore that you can't actually call a list from eventhandlers, but googling this issue just comes up with random OTHER things like calling events in methods and making a list within the event handler).
I just have no idea how to do this, as I said I'm super new to coding and while I've watched like 40 hours of Pluralsight courses, I'm still really confused heh.
I'm assuming there's some simple mistake I made or giant thing I'm missing, and I'm not here to cheat my assessment, just because I've been sitting here for several hours trying to figure this out on google and looking back through classes and can't actually find an answer. Thanks so much for any help anyway :)
If you want to make your life easier, sack off the custom class entirely and load the data into a suitably prepared DataTable instead (i.e. make a DataTable, add relevant named and typed columns to it, load it with rows) and then set the .DataSource of the DataGridView to be the datatable.
Ensure AutoGenerateColumns = true on the DataGridView
Im making a program with many many pages... and in my design, the buttons will eventually get stacked up, so its getting harder and harder to work the more there are.
This question is a clone of this topic.
However, i didn't really get the answer since they were talking about xaml and wpf.
I've also tried to make multiple windows forms, hide and show them to split it up.
But when i hide and show a wndow, its very very easily to see the GUI fading in and out which looks ugly.
I want an instant hide/show function so it looks like its just 1 program with 1 window and now switching.
So what are the tecniques to make a big windowsforms program more managable?
You may create a couple of UserControls, organize and separate your logic on them. Then you may use String Array to store your control names and iterate througth items to display apropriate view. The form may have Panel as container and Dock Style defined as Fill. The simplefied code will look like:
public class MyContainer:Control
{
public MyContainer(string szControlName, UserControl nControl)
{
UserControlName = szControlName; MyControl = nControl;
}
public string UserControlName { get; set; }
public UserControl MyControl { get; set; }
}
In main form:
public List<MyContainer> MyNavigationArray;
public void InitArray()
{
MyNavigationArray = new List<MyContainer>(MyFormPageCount);
for (int i = 0; i < MyFormPageCount; i++)
{
MyNavigationArray.Add(new MyContainer(TheNameOfUserControl, new PredefinedUserControl()));
}
}
private void NextButton_click(object sender, EventArgs e)
{
MyContainer mYcn = this.panel1.Controls[0] as MyContainer;
int nCurPos = MyNavigationArray.IndexOf(mYcn);
if (nCurPos < MyNavigationArray.Count)
{
panel1.Controls.Clear();
MyContainer c = MyNavigationArray[nCurPos + 1];
panel1.Controls.Add(c);
c.Dock = DockStyle.Fill;
}
}
All information stored inside user controls will be in your array and can be used later.
Hope this helped.
I have a listview (lvMap) with 3 columns (Map, From, To) I am trying to write a method that is called as soon as my form loads. this method should look at the listview items and and sort them only by 2 columns "Map" and "From" in ascending order, i dont want it to sort the "To" column. I have written the code below but it sorts every single column, is there a way to leave a column out of the sorting procedure. Thanks.
private void sortListViewOrder()
{
lvMappings.Sorting = SortOrder.Ascending;
lvMappings.Sort();
}
I would suggest you consult the following MSDN article, hopeful it answers your question:
http://support.microsoft.com/kb/319401
Basically you need to create a ListViewColumnSorter instance and add it to your ListView control.
From there on the article will have enough information :)
You have to do it using ListViewColumnSorter . Following KB Link has the sample code to do that.
http://support.microsoft.com/kb/319401
You can assign the column to be sorted using,
Create an instance of a ListView column sorter and assign it
// to the ListView control.
lvwColumnSorter = new ListViewColumnSorter();
this.listView1.ListViewItemSorter = lvwColumnSorter;
lvwColumnSorter.SortColumn = Column;
I needed this feature, or function, in the ListView control. The suggestion to use an Extension Class I first saw here. I tried it and it worked, but only now I can tell how to easily do it. Refer to this reference question:
How to prevent flickering in ListView when updating a single ListViewItem's text?
Step 1: Create a (separate) ControlExtensions class in your project, and paste this code:
using System.Reflection;
using System.Windows.Forms;
namespace [YourNameSpace]
{
public static class ControlExtensions
{
public static void DoubleBuffering(this Control control, bool enable)
{
var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
}
}
}
Step 2: Define the following in the WinForms that has the ListView:
private ListViewColumnSorter lvwColumnSorter = null;
After InitializeComponent(); section, define the following:
lvwColumnSorter = new ListViewColumnSorter();
this.lvwRunningProcesses.ListViewItemSorter = lvwColumnSorter;
lvwColumnSorter._SortModifier = ListViewColumnSorter.SortModifiers.SortByText;
Step 3: In the Form Load event, add these lines after the List View is populated:
// Sort in ascending order Column 0
lvwColumnSorter.SortColumn = 0;
lvwColumnSorter.Order = SortOrder.Ascending;
this.lvwRunningProcesses.Sort();
That's it!