How to get the option from CMFCPropertyGridProperty ComboBox? - visual-c++

How to get option from ComboBox of CMFCPropertyGridProperty class
CMFCPropertyGridProperty* pShpType_S = new CMFCPropertyGridProperty(_T("shpType_S"), shpType_S, _T("shpType_S"));
pShpType_S->AddOption(_T("POINT"));
pShpType_S->AddOption(_T("LINE"));
pShpType_S->AddOption(_T("BOX"));
pShpType_S->AddOption(_T("CIRCLE"));
pShpType_S->AddOption(_T("SPHERE"));
pShpType_S->AddOption(_T("MESH"));
pShpType_S->AllowEdit(FALSE);

You should be able to use:
CMFCPropertyGridProperty* pProperty = ...;
COleVariant vProperty = pProperty->GetValue();
if (vProperty.vt == VT_BSTR)
{
CString strValue = vProperty.bstrVal;
// If you also want the index
bool bFound = false;
int iNumOptions = pProperty->GetOptionCount();
for (int iOption = 0; iOption < iNumOptions; iOption++)
{
if (strValue.CollateNoCase(pProperty->GetOption(iOption)) == 0)
{
// Match!
bFound = true;
break;
}
}
// iOption has the index value.
}
Lookup:
CMFCPropertyGridProperty::GetValue
CMFCPropertyGridProperty::GetOption
CMFCPropertyGridProperty::GetOptionCount
COleVariant
The variant can contain several different types of values and you have to check the .vt member. For combo properties the value will be a text string, so you check for [VT_BSTR][1].
The actual definition of [CMFCPropertyGridProperty::GetValue][1] is:
virtual const _variant_t& GetValue() const;
So you can also use _variant_t instead of COleVariant. But I use the latter.

Related

Will it be simpler to convert this code from CStringArray to std::vector<CString>?

Given this code:
void CSelectNamesDlg::ShuffleArray(CString strName, CStringArray *pAryStrNames)
{
if (pAryStrNames == nullptr)
return;
const auto iSize = pAryStrNames->GetSize();
if (iSize > 1)
{
// First, we must locate strName in the array
auto i = CSelectNamesDlg::LocateText(strName, pAryStrNames);
if (i != -1)
{
const auto iName = i;
// We must now shuffle the names from the bottom to the top
const auto iCount = gsl::narrow<int>(iSize) - iName;
for (i = 0; i < iCount; i++)
{
CString strTemp = pAryStrNames->GetAt(iSize-1);
pAryStrNames->RemoveAt(iSize-1);
pAryStrNames->InsertAt(0, strTemp);
}
}
}
}
int CSelectNamesDlg::LocateText(CString strText, const CStringArray *pAryStrText)
{
bool bFound = false;
int i{};
if (pAryStrText != nullptr)
{
const auto iSize = pAryStrText->GetSize();
for (i = 0; i < iSize; i++)
{
if (pAryStrText->GetAt(i) == strText)
{
// Found him!
bFound = true;
break;
}
}
}
if (!bFound)
i = -1;
return (int)i;
}
If I convert my CStringArray into a std::vector<CString is it going to be simpler to achieve the same PerformShuffle and LocateText methods?
Should I just stay with CStringArray?
I know of 1 (ONE!) benefit of MFC array over std::vector - they support MFC-style serialization. If you use it - you may be stuck.
However, if you don't - I would use std::vector<CString>. Your LocateText (that is overly verbose) will become obsolete - just use find
Also, your ShuffleArray is very inefficient (remove/insert one item at a time). Using a vector will allow you to do something like Best way to extract a subvector from a vector?

Changing the Default Option for Column Filters

Is it possible to change the default option all column filters? I'm pretty sure I can accomplish this with some JavaScript, but I'd like to know if there's any way within the Acumatica framework to change this.
The answer will be no. The filter is inside the PX.Web.UI.dll in the PXGridFilter class which is an internal class. The property that you are interested in is the Condition.
The value is set inside one of the private methods of the PXGrid class. The code of the method is below:
private IEnumerable<PXGridFilter> ab()
{
List<PXGridFilter> list = new List<PXGridFilter>();
if (this.FilterID != null)
{
Guid? filterID = this.FilterID;
Guid guid = PXGrid.k;
if (filterID == null || (filterID != null && filterID.GetValueOrDefault() != guid))
{
using (IEnumerator<PXResult<FilterRow>> enumerator = PXSelectBase<FilterRow, PXSelect<FilterRow, Where<FilterRow.filterID, Equal<Required<FilterRow.filterID>>, And<FilterRow.isUsed, Equal<True>>>>.Config>.Select(this.DataGraph, new object[]
{
this.FilterID.Value
}).GetEnumerator())
{
while (enumerator.MoveNext())
{
FilterRow row = enumerator.Current;
string dataField = row.DataField;
PXCache pxcache = PXFilterDetailView.TargetCache(this.DataGraph, new Guid?(this.FilterID.Value), ref dataField);
if (this.Columns[row.DataField] != null)
{
List<PXGridFilter> list2 = list;
int valueOrDefault = row.OpenBrackets.GetValueOrDefault();
string dataField2 = row.DataField;
string dataField3 = row.DataField;
int value = (int)row.Condition.Value;
object value2 = pxcache.ValueFromString(dataField, row.ValueSt);
string valueText = row.ValueSt.With((string _) => this.Columns[row.DataField].FormatValue(_));
object value3 = pxcache.ValueFromString(dataField, row.ValueSt2);
string value2Text = row.ValueSt2.With((string _) => this.Columns[row.DataField].FormatValue(_));
int valueOrDefault2 = row.CloseBrackets.GetValueOrDefault();
int? #operator = row.Operator;
int num = 1;
list2.Add(new PXGridFilter(valueOrDefault, dataField2, dataField3, value, value2, valueText, value3, value2Text, valueOrDefault2, #operator.GetValueOrDefault() == num & #operator != null));
}
}
return list;
}
}
}
if (this.FilterRows != null && this.FilterRows.Count > 0)
{
for (int i = 0; i < this.FilterRows.Count; i++)
{
PXFilterRow row = this.FilterRows[i];
list.Add(new PXGridFilter(row.OpenBrackets, row.DataField, row.DataField, (int)row.Condition, row.Value, row.Value.With(delegate(object _)
{
if (this.Columns[row.DataField] == null)
{
return _.ToString();
}
return this.Columns[row.DataField].FormatValue(_);
}), row.Value2, row.Value2.With(delegate(object _)
{
if (this.Columns[row.DataField] == null)
{
return _.ToString();
}
return this.Columns[row.DataField].FormatValue(_);
}), row.CloseBrackets, row.OrOperator));
}
}
return list;
}
UPDATE
The below JS code allows you to change the condition of the last set filter:
px_all.ctl00_phG_grid_fd.show();
px_all.ctl00_phG_grid_fd_cond.items.items[7].setChecked(true);
px_all.ctl00_phG_grid_fd_ok.element.click();
px_all.ctl00_phG_grid_fd_cond.items.items[7].value "EQ"
px_all.ctl00_phG_grid_fd_cond.items.items[8].value "NE"
px_all.ctl00_phG_grid_fd_cond.items.items[9].value "GT"
px_all.ctl00_phG_grid_fd_cond.items.items[13].value "LIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[14].value "LLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[15].value "RLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[16].value "NOTLIKE"
px_all.ctl00_phG_grid_fd_cond.items.items[18].value "ISNULL"
px_all.ctl00_phG_grid_fd_cond.items.items[19].value "ISNOTNULL"

Is there a way to use normal ASCII characters (like a comma) as wxWidgets menu accelerators?

I want a few menu entries that show accelerators that are normal keys, like the space-bar or comma key, but I don't want wxWidgets to make those accelerators itself (because then they can't be used anywhere in the program, including in things like edit boxes).
Unfortunately, wxWidgets insists on always making anything it recognizes in that column into an accelerator under its control, and simply erases anything it doesn't recognize.
I'm looking for some way to either put arbitrary text into the accelerator column (which I don't think exists, I've looked at the source code), or get 'hold of the accelerator table used for the menus so I can modify it myself (haven't found it yet). Can anyone point me in the right direction?
You can try wxKeyBinder. It allows you to bind hotkeys to commands (usually menu entries), save/load/add/remove/modify ... them easily
I couldn't find a way to access the menu's accelerator keys directly, but modifying the accelerator menu text works just as well. Here's the code I came up with:
In a header file:
class accel_t {
public:
// If idcount == -1, idlist must be null or terminated with a -1 entry.
accel_t(): mMenu(0) { }
accel_t(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void reset(wxMenuBar *m, int *idlist = 0, int idcount = -1);
void restore() const;
void remove() const;
private: //
struct accelitem_t {
accelitem_t(int _id, wxAcceleratorEntry _key): id(_id), hotkey(_key) { }
int id;
wxAcceleratorEntry hotkey;
};
typedef std::vector<accelitem_t> data_t;
void noteProblemMenuItems(wxMenu *m);
static bool isProblemAccelerator(wxAcceleratorEntry *a);
wxMenuBar *mMenu;
data_t mData;
};
In a cpp file:
accel_t::accel_t(wxMenuBar *m, int *idlist, int idcount) {
reset(m, idlist, idcount);
}
void accel_t::reset(wxMenuBar *m, int *idlist, int idcount) {
mMenu = m;
mData.clear();
if (idlist == 0) {
for (int i = 0, ie = m->GetMenuCount(); i != ie; ++i)
noteProblemMenuItems(m->GetMenu(i));
} else {
if (idcount < 0) {
int *i = idlist;
while (*i != -1) ++i;
idcount = (i - idlist);
}
for (int *i = idlist, *ie = i + idcount; i != ie; ++i) {
wxMenuItem *item = mMenu->FindItem(*i);
if (item) {
wxAcceleratorEntry *a = item->GetAccel();
if (a != 0) mData.push_back(accelitem_t(*i, *a));
}
}
}
}
bool accel_t::isProblemAccelerator(wxAcceleratorEntry *a) {
if (a == 0) return false;
int flags = a->GetFlags(), keycode = a->GetKeyCode();
// Normal ASCII characters, when used with no modifier or Shift-only, would
// interfere with editing.
if ((flags == wxACCEL_NORMAL || flags == wxACCEL_SHIFT) &&
(keycode >= 32 && keycode < 127)) return true;
// Certain other values, when used as normal accelerators, could cause
// problems too.
if (flags == wxACCEL_NORMAL) {
if (keycode == WXK_RETURN ||
keycode == WXK_DELETE ||
keycode == WXK_BACK) return true;
}
return false;
}
void accel_t::noteProblemMenuItems(wxMenu *m) {
// Problem menu items have hotkeys that are ASCII characters with normal or
// shift-only modifiers.
for (size_t i = 0, ie = m->GetMenuItemCount(); i != ie; ++i) {
wxMenuItem *item = m->FindItemByPosition(i);
if (item->IsSubMenu())
noteProblemMenuItems(item->GetSubMenu());
else {
wxAcceleratorEntry *a = item->GetAccel();
if (isProblemAccelerator(a))
mData.push_back(accelitem_t(item->GetId(), *a));
}
}
}
void accel_t::restore() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
wxString hotkey = i->hotkey.ToString();
if (hotkey.empty()) {
// The wxWidgets authors apparently don't expect ASCII
// characters to be used for accelerators, because
// wxAcceleratorEntry::ToString just returns an empty string for
// them. This code deals with that.
int flags = i->hotkey.GetFlags(), key = i->hotkey.GetKeyCode();
if (flags == wxACCEL_SHIFT) hotkey = wx("Shift-") + wxChar(key);
else hotkey = wxChar(key);
}
item->SetItemLabel(text + '\t' + hotkey);
}
}
}
void accel_t::remove() const {
if (mMenu == 0) return;
for (data_t::const_iterator i = mData.begin(), ie = mData.end(); i != ie;
++i)
{
wxMenuItem *item = mMenu->FindItem(i->id);
if (item) {
wxString text = item->GetItemLabel().BeforeFirst(wxT('\t'));
item->SetItemLabel(text);
}
}
}
The easiest way to use it is to create your menu-bar as normal, with all accelerator keys (including the problematic ones) in place, then create an accel_t item from it something like this:
// mProblemAccelerators is an accel_t item in the private part of my frame class.
// This code is in the frame class's constructor.
wxMenuBar *menubar = _createMenuBar();
SetMenuBar(menubar);
mProblemAccelerators.reset(menubar);
It will identify and record the accelerator keys that pose problems. Finally, call the remove and restore functions as needed, to remove or restore the problematic accelerator keys. I'm calling them via messages passed to the frame whenever I open a window that needs to do standard editing.

how to differentiate a Folder Type(Windows/FTP) in MFC(VC++)?

how to differentiate a Folder Type(Windows/FTP) in MFC(VC++)?
In my case I have a MFC Treeview in which i am adding list of folders coming from server , that folders are Windows and CIFS .Now i want to differentiate internally what type of folder are they because i select some folder and say connect then it brings up login page where i need to login credentials details , but before that i need to differentiate what type of folder is that ?
Any treeview property like setting and geeting key or any other approach.
Thanks in Advance.
I get folder list from server in this format volume1/Folder1,volume2/Folder2||Folder3,Folder4 in this below method i am removing "||" and maintaining them in two differnt variables: strDataFolder contains - FTP Folders and
strNASfolders conatins CIFS folder , then one more method i am using UpdateFolder. If it is FTP then directly Add but if it CIFS check for duplication if that folder is already there dont add it again to treeview.
Now iam not able to get it how to differentiate what type of folder are they ?
void CNDSClientDlg::UpdateSharedFolder(CString strNASfolders,HTREEITEM hChildItem)
{
CString strDataFolder = "";
CString strData = "";
int iPos = 0;
int fPosition = 0;
fPosition = strNASfolders.Find("||");
if(fPosition != -1)
{
strDataFolder = strNASfolders.Mid(0,fPosition);
strNASfolders = strNASfolders.Right(strNASfolders.GetLength()-(fPosition+2));
}
else
{
strDataFolder = strNASfolders;
}
if (strDataFolder != "" )
{
UpdateFolders(strDataFolder,hChildItem,false);
}
if (strNASfolders != "" ) //don't add if already exist
{
UpdateFolders(strNASfolders,hChildItem,true);
}
}
void CNDSClientDlg::UpdateFolders(CString strFolderType,HTREEITEM hChildItem,bool bCheck)
{
int iPos = 0 ;
CString strData = "";
CString strCurrFolder ="";
HTREEITEM HShareFolder = NULL;
bool bFound = false ;
while (iPos != -1)
{
iPos = strFolderType.Find(",");
if (iPos != -1)
{
strData = strFolderType.Mid(0,iPos);//get the folder details
}
else
{
strData = strFolderType;//get the last folder details
}
strFolderType = strFolderType.Right(strFolderType.GetLength()-(iPos+1)); //get remaining data
int fPos = strData.ReverseFind('/');
if (fPos != -1)
{
strCurrFolder = strData.Mid(fPos+1,strData.GetLength()); // get required data
}
else
{
strCurrFolder = strData; //else assign all the data
}
if(bCheck == true)
{
bFound = false ;
HTREEITEM hTempItem = NULL;
CString strItemText = "" ;
hTempItem = m_pTreeview->GetChildItem(hChildItem);
strItemText = m_pTreeview->GetItemText(hTempItem);
while(hTempItem != NULL)
{
if(strItemText != strCurrFolder)
{
strItemText = m_pTreeview->GetItemText(hTempItem);
hTempItem = m_pTreeview->GetNextSiblingItem(hTempItem);
}
else
{
bFound = true;
break;
}
}
}
if(bCheck == false || bFound == false)
{
HShareFolder = m_pTreeview->InsertItem(strCurrFolder,hChildItem);
m_pTreeview->SetItemImage(HShareFolder,2,2);
TTipItemData* pToolTipData ;
pToolTipData = new TTipItemData;
pToolTipData->strTool = strData ;
m_pTreeview->SetItemData(HShareFolder,DWORD(pToolTipData));
}
m_pTreeview->Expand(hParentItem,TVE_EXPAND);
m_pTreeview->EnsureVisible(hParentItem);
}
}
Items in treeviews can have arbitrary data associated with them. Check out:
http://msdn.microsoft.com/en-us/library/ettyybhw(VS.80).aspx
The InsertItem method shown here has an LPARAM parameter. This is for your use, and you can set this to some value that is meaningful to your application.
(EDIT: Alternatively, use one of the least convoluted overloads to insert your item and use CTreeCtrl::SetItemData on the handle that is returned afterwards).
To find out what value is associated with your item, use CTreeCtrl::GetItemData.
Minor Example:
HTREEITEM hItem = m_ctrlTree.InsertItem("My Item", TVI_ROOT);
HTREEITEM hOther = m_ctrlTree.InsertItem("Child Item", hItem);
m_ctrlTree.SetItemData(hItem, static_cast<DWORD_PTR>(10)); // set LPARAM to 10
// note, you can also store pointers! This assumes pObj is some kind of instance
// of a class.
m_ctrlTree.SetItemData(hOther, static_cast<DWORD_PTR>(pObj));
// at a later point:
int myVal = static_cast<int>(m_ctrlTree.GetItemData(hItem));
MyObject* pObj = static_cast<MyObject*>(m_ctrlTree.GetItemData(hOther));

Easy way to search a string for strings

I'm trying to find the easiest way to search a string for an array of possible strings. I know the easy way to do this for characters is to use myString.IndexOfAny(charArray). But how what if I'd like to search my string for strings and not just characters? Are there any .net tricks or methods that make this easier?
Basically, I'd like to do something like this:
string myName = "rahkim";
string[] names = new string[] {"joe","bob","chris"};
if(myName.IndexOfAny(names) >= 0)
{
//success code//
}
I know there are ways to do this with loops, etc. But I was hoping for something inherent in the framework.
You should define if you want to to find equal strings or search for a matching substring. Both ways are easy pre-LINQ and with LINQ.
string myName = "rahkim";
string[] names = new string[] { "joe", "bob", "chris" };
Equal Strings, LINQ
bool contains = names.Contains(myName);
Equal Strings, Pre-LINQ
bool contains = new List<string>(name).Contains(myName);
Substrings, LINQ
bool contains = names.Any(name => name.Contains(myName));
Substring, Pre-LINQ
bool contains = false;
foreach(string name in names)
if (name.Contains(myName))
contains = true;
If anyone else found this while trying to search for a .Net method like String.IndexOfAny(String[]), this is my solution:
C#
public int IndexOfAny(string test, string[] values)
{
int first = -1;
foreach (string item in values) {
int i = test.IndexOf(item);
if (i >= 0) {
if (first > 0) {
if (i < first) {
first = i;
}
} else {
first = i;
}
}
}
return first;
}
VB
Public Function IndexOfAny(test As String, values As String()) As Integer
Dim first As Integer = -1
For Each item As String In values
Dim i As Integer = test.IndexOf(item)
If i >= 0 Then
If first > 0 Then
If i < first Then
first = i
End If
Else
first = i
End If
End If
Next
Return first
End Function
You can do a LastIndexOfAny(String[]) by just switching the
i < first
to
i > first
You can (also) use the static IndexOf method of the Array class:
bool hasName = Array.IndexOf(names, myName) > -1;
int IndexOfAny(String[] rgs) would indeed be nice but it's nominally an O(n^2) operation. If, in your application, the set of strings rgs is large and always the same, the most efficient approach is to load them into a trie data structure once, and then use the trie repeatedly to search for them within the unknown strings given at runtime.
Here is the relevant code, adapted from a C# trie source I found on the web, attributed to "Kerry D. Wong." In my version, each string in the trie has a "payload" of generic type TValue. To use this trie to simply search for substrings, the payload could always be set to true, as illustrated with simple_trie.
The other thing I changed here is that this trie automatically adapts allow for storage of arbitrary Unicode strings. The array at each node—which characterizes a trie—adjusts its base and length to accomodate the range of Unicode characters which need to be stored at that node. This allows for case-sensitive matching, for example.
The C# 3.0 initialization syntax is handy for this trie, but enabling it requires a dummy implementation of IEnumerable in order to compile. The CLR doesn't seem to call GetEnumerator() and I suggest that you don't try to enumerate with its result either.
using System;
using System.Collections.Generic;
using System.Linq; // only used in Main()
class Program
{
// trie with payload of type <String>
static Trie<String> value_trie = new Trie<String>
{
{ "rabbit", "cute" },
{ "giraffe", "tall" },
{ "ape", "smart" },
{ "hippo", "large" },
};
// degenerate case of a trie without payload
static Trie<bool> simple_trie = new Trie<bool>
{
{ "rabbit", true },
{ "giraffe", true },
{ "ape", true },
{ "hippo", true },
};
static void Main(String[] args)
{
String s = "Once upon a time, a rabbit met an ape in the woods.";
// Retrieve payloads for words in the string.
//
// output:
// cute
// smart
foreach (String word in value_trie.AllSubstringValues(s))
Console.WriteLine(word);
// Simply test a string for any of the words in the trie.
// Note that the Any() operator ensures that the input is no longer
// traversed once a single result is found.
//
// output:
// True
Console.WriteLine(simple_trie.AllSubstringValues(s).Any(e=>e));
s = "Four score and seven years ago.";
// output:
// False
Console.WriteLine(simple_trie.AllSubstringValues(s).Any(e => e));
}
}
class TrieNode<TValue>
{
private TrieNode<TValue>[] nodes = null;
private TValue m_value = default(TValue);
private Char m_base;
public Char Base { get { return m_base; } }
public bool IsEnd { get { return !m_value.Equals(default(TValue)); } }
public TValue Value
{
get { return m_value; }
set { m_value = value; }
}
public IEnumerable<TrieNode<TValue>> Nodes { get { return nodes; } }
public TrieNode<TValue> this[char c]
{
get
{
if (nodes != null && m_base <= c && c < m_base + nodes.Length)
return nodes[c - m_base];
return null;
}
}
public TrieNode<TValue> AddChild(char c)
{
if (nodes == null)
{
m_base = c;
nodes = new TrieNode<TValue>[1];
}
else if (c >= m_base + nodes.Length)
{
Array.Resize(ref nodes, c - m_base + 1);
}
else if (c < m_base)
{
Char c_new = (Char)(m_base - c);
TrieNode<TValue>[] tmp = new TrieNode<TValue>[nodes.Length + c_new];
nodes.CopyTo(tmp, c_new);
m_base = c;
nodes = tmp;
}
TrieNode<TValue> node = nodes[c - m_base];
if (node == null)
{
node = new TrieNode<TValue>();
nodes[c - m_base] = node;
}
return node;
}
};
class Trie<TValue> : System.Collections.IEnumerable
{
private TrieNode<TValue> _root = new TrieNode<TValue>();
// This dummy enables C# 3.0 initialization syntax
public System.Collections.IEnumerator GetEnumerator()
{
return null;
}
public void Add(String s, TValue v)
{
TrieNode<TValue> node = _root;
foreach (Char c in s)
node = node.AddChild(c);
node.Value = v;
}
public bool Contains(String s)
{
TrieNode<TValue> node = _root;
foreach (Char c in s)
{
node = node[c];
if (node == null)
return false;
}
return node.IsEnd;
}
public TValue Find(String s_in)
{
TrieNode<TValue> node = _root;
foreach (Char c in s_in)
{
node = node[c];
if (node == null)
return default(TValue);
}
return node.Value;
}
public IEnumerable<TValue> FindAll(String s_in)
{
TrieNode<TValue> node = _root;
foreach (Char c in s_in)
{
node = node[c];
if (node == null)
break;
if (node.Value != null)
yield return node.Value;
}
}
public IEnumerable<TValue> AllSubstringValues(String s)
{
int i_cur = 0;
while (i_cur < s.Length)
{
TrieNode<TValue> node = _root;
int i = i_cur;
while (i < s.Length)
{
node = node[s[i]];
if (node == null)
break;
if (node.Value != null)
yield return node.Value;
i++;
}
i_cur++;
}
}
};
Here's the right syntax:
if(names.Contains(myName))
{
//success code//
}
if (names.Contains(myName))
{
//success code//
}

Resources