I work for a call centre that has been using QAS Pro for near 2 years now. We use a resource DLL inside access databases to talk to the internally hosted QAS server. Its only use is to gather address details based on postcode. So the first function gets a list of address from that postcode, inserts them into a combo box in access. After the operator can select the appropriate address and it inserts it into the correct fields.
This was written by a developer who is no longer with us. Its my job to fix the code. With some testing I've been able to verify it is the c# code we use and not the addresses. As the test harness works fine.
The resource DLL uses the c# test code from QAS with an extra file for a few functions. I'm new to c# and have never worked on something like this before. Any help is appreciated.
This is the code written by an old colleague.
namespace MangoQAS
{
using com.qas.proweb;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[ComVisible(true)]
public class QAS
{
public QAS()
{
QuickAddress address = new QuickAddress("http://10.10.15.7:2021") {
Engine = QuickAddress.EngineTypes.Singleline,
Flatten = true
};
this.searchService = address;
}
private string GetMoniker(string p)
{
return this.searchService.Search("GBR", p, PromptSet.Types.Default, "Database layout").Picklist.Items[0].Moniker;
}
public string[] RefinePostcode(string p)
{
string moniker = this.GetMoniker(p);
FormattedAddress formattedAddress = this.searchService.GetFormattedAddress(moniker, "Database Layout");
return new string[] { formattedAddress.AddressLines[0].Line, formattedAddress.AddressLines[1].Line, formattedAddress.AddressLines[2].Line, formattedAddress.AddressLines[3].Line, formattedAddress.AddressLines[4].Line, formattedAddress.AddressLines[5].Line, formattedAddress.AddressLines[6].Line };
}
public string[] SearchPostcodes(string postCode)
{
SearchResult result = this.searchService.Search("GBR", postCode, PromptSet.Types.OneLine, "Database layout");
string[] strArray = new string[result.Picklist.Length];
for (int i = 0; i < result.Picklist.Length; i++)
{
strArray[i] = result.Picklist.Items[i].Text;
}
return strArray;
}
private QuickAddress searchService { get; set; }
}
}
SearchPostcodes - Brings back a list of addresses based on the postcode.
RefinePostcode - takes the address line and sends back a formatted address.
The problem seems to be with RefinePostcode. I have tried formatting the address string as my first thought was it didn't like forward slashes. This did not work.
For example, Using the Postcode: PA169AE.
This gives me: 0/1 15 Brachelston Street, GREENOCK, Renfrewshire, at the top of the combobox.
If I click on this address it will send back: 1 Crossgates, Greenock Road, PA7 5JU.
Changing everything including the postcode I entered.
I believe the problem is with RefinePostcode or GetMoniker. The 2 blocks below are from the sample code and unchanged, but may be required to diagnose.
public FormattedAddress GetFormattedAddress(string sMoniker, string sLayout)
{
Debug.Assert((sMoniker != null) && (sLayout != null));
QAGetAddress qAGetAddress = new QAGetAddress {
Layout = sLayout,
Moniker = sMoniker,
QAConfig = this.m_Config,
Language = this.m_LanguageString
};
FormattedAddress address2 = null;
try
{
address2 = new FormattedAddress(this.SearchService.DoGetAddress(qAGetAddress).QAAddress);
}
catch (Exception exception)
{
this.MapException(exception);
}
return address2;
}
public SearchResult Search(string sDataID, string sSearch, PromptSet.Types tPromptSet, string sLayout)
{
Debug.Assert(sDataID != null);
Debug.Assert(sSearch != null);
QASearch qASearch = new QASearch {
Country = sDataID,
Engine = this.m_Engine
};
qASearch.Engine.PromptSet = (PromptSetType) tPromptSet;
qASearch.Engine.PromptSetSpecified = true;
qASearch.Layout = sLayout;
qASearch.QAConfig = this.m_Config;
qASearch.Search = sSearch;
qASearch.Language = this.m_LanguageString;
SearchResult result = null;
try
{
result = new SearchResult(this.SearchService.DoSearch(qASearch));
}
catch (Exception exception)
{
this.MapException(exception);
}
return result;
}
I've thoroughly searched Google and cant seem to find any reason this would happen. I can post more code samples if required.
did you figure this out?
From the looks of it, I think the problem you have here is the context of the search. Both the QAS server, and QuickAddress class are stateless - they have no history of previous searches.
Because of this at the moment there is nothing linking your first search for postcodes, and your second search on the address line. So, when you call RefinePostcode you are not refining at all. You are instead performing a brand new search UK wide on "0/1 15 Brachelston Street, GREENOCK, Renfrewshire". Whilst QAS pro can handle this, this search generates a few possibilities so the result you are after is not the first one returned.
There are a few possibilities here to improve your workflow. To avoid making any changes to your VBA code and just limit the changes to the sample above you could introduce state to this class and change the workflow so that you are passing in the postcode with your search. Similar to the following:
public class QAS
{
public QAS()
{
// Create a new soap proxy that can talk to QAS Pro Web
QuickAddress address = new QuickAddress("http://10.10.15.7:2021")
{
Engine = QuickAddress.EngineTypes.Singleline,
Flatten = true
};
this.searchService = address;
}
/// <summary>
/// For the supplied search, get the moniker which can then be used to format the address.
/// </summary>
private string GetMoniker(string p)
{
return this.searchService.Search("GBR", p, PromptSet.Types.Default, "Database layout").Picklist.Items[0].Moniker;
}
/// <summary>
/// Return a formatted address from the supplied search.
/// </summary>
public string[] RefinePostcode(string p)
{
// Append the postcode to our address to speed up and improve searches.
string search = p + "," + Postcode;
SearchResult result = this.searchService.Search("GBR",
postCode,
PromptSet.Types.OneLine,
"Database layout");
if ( result.Picklist.Items.Length > 0 )
{
}
else
{
// What is your workflow if an address is not found?
}
string moniker = this.GetMoniker(search);
FormattedAddress formattedAddress = this.searchService.GetFormattedAddress(moniker, "Database Layout");
return new string[] { formattedAddress.AddressLines[0].Line, formattedAddress.AddressLines[1].Line, formattedAddress.AddressLines[2].Line, formattedAddress.AddressLines[3].Line, formattedAddress.AddressLines[4].Line, formattedAddress.AddressLines[5].Line, formattedAddress.AddressLines[6].Line };
}
/// <summary>
/// Once a postcode is captured by the operator, return a list of potential addresses.
/// </summary>
public string[] SearchPostcodes(string postCode)
{
Postcode = postcode;
SearchResult result = this.searchService.Search("GBR",
postCode,
PromptSet.Types.OneLine,
"Database layout");
string[] strArray = new string[result.Picklist.Length];
for (int i = 0; i < result.Picklist.Length; i++)
{
strArray[i] = result.Picklist.Items[i].Text;
}
return strArray;
}
private QuickAddress searchService { get; set; }
/// <summary>
/// Gets or sets the postcode from the initial search.
/// </summary>
private string Postcode
{
get;
set;
}
}
If you have the time though, it would be better to go a little further and properly fix your workflow. Context between searches in QAS Pro Web is handled through monikers. These are tokens that Pro Web provides after all interactions with the server that you can then use to either get formatted addresses or perform further searching with. There are more details on this in the documentation provided.
After your initial search on the postcode, you get a list of addresses and each of these has a moniker associated with it. If you are able to store these monikers and associate these with the list you put into you combo box then when an operator selects one you can pass the moniker straight into GetFormatted address and you should be able to capture addresses quicker, more accurately and with less code!
Hope that helps!
Al.
This is the code access uses.
Private Sub cmdSelect_Click()
Dim thisOne As String
thisOne = Me.lkupAddressList.Value
Dim objQAS As MangoQAS.QAS
Set objQAS = New MangoQAS.QAS
Dim finalAddress As Variant
finalAddress = objQAS.RefinePostcode(thisOne)
Form_Script.txtAddress1 = finalAddress(0)
Form_Script.txtAddress2 = finalAddress(1)
Form_Script.txtAddress3 = finalAddress(2)
Form_Script.txtTown = finalAddress(3)
Form_Script.txtCounty = finalAddress(4)
Form_Script.txtPostcode = finalAddress(5)
Set objQAS = Nothing
DoCmd.Close acForm, "frmSelectAddress_qas"
End Sub
Private Sub Form_Load()
Dim postcodeToSearch As String
postcodeToSearch = Form_Script.txtPostcode
Dim objQAS As MangoQAS.QAS
Set objQAS = New MangoQAS.QAS
Dim results As Variant
results = objQAS.SearchPostcodes(postcodeToSearch)
Dim howMany As Integer
howMany = UBound(results)
For i = 0 To howMany
Me.lkupAddressList.AddItem ("'" & results(i) & "'")
Next
Set objQAS = Nothing
End Sub
Related
Good afternoon,
My first time posting after using this website for years, so I apologize for formatting and probably a dumb question I can't find the answer for... I am scanning a QR Code that contains the JSON format below and am trying to populate a "device details page" with the full object's data from the ZtdId value from the JSON received from the scanned code. The parameter passes along fine to the new page, but the screen will be black.
JSON Format:
{"Version":"1.0.0","Id":"5ca169f7-9e7b-4861-abf5-859cdb468f6e","ZtdId":"dfb0527f-64d1-4f78-85ff-50cd6fab7e6c","PKID":"","SerialNumber":"","SequenceBlockNumber":1,"TotalBlocks":1}
Black navigated page example
I have a separate collectionview page that navigates to the same "device details page" and works fine. I've tried quite a number of combinations from the code referenced below in the hyperlink and across the internet, though, I can't get the page to load at all. I realized later in my fumbling that it wasn't loading the object and placed an "if the serialnumber is blank.." operator to connect it to the data.
I've even tried passing it to a blank or about page and it loads the page fine...What am I doing wrong here? (I can confirm if I write in a bunch of Console.WriteLine entries I can expose all the fields of the device I'm trying to load from the scanned code.) I've tried going through the "Xanimals" and many other sample solutions, from the sample github repositories, but can't seem to identify why the page comes up black.
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/shell/navigation?view=net-maui-7.0#pass-data
Scan Page relevant code:
async void BarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
try
{
var first = e.Results?.FirstOrDefault();
if (first is not null)
{
Dispatcher.Dispatch(() =>
{
barcodeGenerator.ClearValue(BarcodeGeneratorView.ValueProperty);
barcodeGenerator.Format = first.Format;
barcodeGenerator.Value = first.Value;
});
}
string Json = JToken.Parse(e.Results[0].Value).ToString();
Models.Device device = JsonConvert.DeserializeObject<Models.Device>(Json);
var navigationParameters = new Dictionary<string, object>
{
{ "Device", device }
};
await Shell.Current.GoToAsync($"devicedetails", navigationParameters);
}
catch (Exception)
{
}
}
Device Detail Page relevant code:
public Models.Device Device { get; private set; }
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
Device = query["Device"] as Models.Device;
if(String.IsNullOrEmpty(Device.SerialNumber))
{
Models.Device device = DeviceData.Devices.FirstOrDefault(a => a.ZtdId == Device.ZtdId);
Device = device;
}
OnPropertyChanged("Device");
}
Device Example:
public static IList<Models.Device> Devices { get; private set; }
static DeviceData()
{
Devices = new List<Models.Device>
{
new Models.Device
{
SerialNumber = "00-000001",
Manufacturer = "Dell, Inc.",
Model = "XPS 13 9360",
PurchaseOrderNumber = "",
GroupTag = "US-EST-001",
DeploymentProfile = "Hybrid AAD",
AddressableUserName = "Jane Doe",
UserPrincipalName = "janedoe#contoso.com",
ZtdId = "dfb0527f-64d1-4f78-85ff-50cd6fab7e6c",
AzureActiveDirectoryDeviceId = Guid.NewGuid().ToString(),
ManagedDeviceId = Guid.NewGuid().ToString()
}
};
}
I have a C# application in which I imported API methods using wsdl, as described in the Softlayer guidelines.
I'm editing virtual guests by passing a Container_Product_Order_Virtual_Guest_Upgrade structure to the Product_Order service.
Everything works great except for when adding item price IDs for guest_disks, scenario in which after about 6-7 seconds the following exception is thrown:
"The request was aborted: The connection was closed unexpectedly."
This happens with both verifyOrder and placeOrder methods.
I checked Virtual_Guest::getUpgradeItemPrices in order to make sure that the guest disk values are valid(even though passing invalid itempriceIds for the VM results in an specific error response, not in a generic exception such as the one described above).
I can't find any details in the documentation that could give me hints as why I can upgrade anything except guest_disks.
EDIT:
Stripped code as requested:
SoftLayer_Virtual_Guest[] _VMtoEditList = new SoftLayer_Virtual_Guest[1] { -- Vm instance details are retrieved from SL according to the passed VM ID; };
List<SoftLayer_Product_Item_Price> _itemPriceList = new List<SoftLayer_Product_Item_Price>();
foreach (-- collection of properties to be upgraded )
{
SoftLayer_Product_Item_Category _category = new SoftLayer_Product_Item_Category();
_category.categoryCode = -- retrieved from the collection on which I iterate (eg "guest_disk0", "ram", etc.);
SoftLayer_Product_Item_Price _itemPrice = new SoftLayer_Product_Item_Price();
_itemPrice.id = -- the item priceID for the current item;
_itemPrice.idSpecified = true;
_itemPrice.categories = new SoftLayer_Product_Item_Category[1] { _category };
_itemPriceList.Add(_itemPrice);
}
SoftLayer_Product_Item_Price[] _itemPricesArray = _itemPriceList.ToArray();
SoftLayer_Container_Product_Order_Property _property1 = new SoftLayer_Container_Product_Order_Property();
_property1.name = "NOTE_GENERAL";
_property1.value = -- order's description;
SoftLayer_Container_Product_Order_Property _property2 = new SoftLayer_Container_Product_Order_Property();
_property2.name = "MAINTENANCE_WINDOW";
_property2.value = "now";
// Build SoftLayer_Container_Product_Order_Property
SoftLayer_Container_Product_Order_Property[] properties = new SoftLayer_Container_Product_Order_Property[2] { _property1, _property2 };
-- create container
SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade _upgradeContainer = new SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade();
_upgradeContainer.virtualGuests = _VMtoEditList;
_upgradeContainer.prices = _itemPricesArray;
_upgradeContainer.properties = properties;
_upgradeContainer.packageId = 46;
_upgradeContainer.packageIdSpecified = true;
SoftLayer_Product_OrderService service = new SoftLayer_Product_OrderService();
-- authentication structure is created here
SoftLayer_Container_Product_Order _verifiedOrder = service.verifyOrder(_upgradeContainer);
service.placeOrder(_verifiedOrder, false);
Here a have an example to upgrade which works see below. I see that in your code you are adding the packageId which is not required, removed and try again.
Also when you are creating the web references try using the last version of the api in the WSDL url (v3.1)
e.g. https://api.softlayer.com/soap/v3.1/SoftLayer_Hardware_Server?wsdl
//-----------------------------------------------------------------------
// <copyright file="PlaceOrderUpgrade.cs" company="Softlayer">
// SoftLayer Technologies, Inc.
// </copyright>
// <license>
// http://sldn.softlayer.com/article/License
// </license>
//-----------------------------------------------------------------------
namespace VirtualGuests
{
using System;
using System.Collections.Generic;
class PlaceOrderUpgrade
{
/// <summary>
/// Order an upgrade for Virtual Guest
/// This script orders an upgrade for Virtual Guest, in this case we will upgrade the ram for a Virtual Guest,
/// It uses SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade container and SoftLayer_Product_Order::placeOrder
/// method for it.
/// For more information, review the following links:
/// </summary>
/// <manualPages>
/// http://sldn.softlayer.com/reference/services/SoftLayer_Product_Order/placeOrder
/// http://sldn.softlayer.com/reference/datatypes/SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade/
/// http://sldn.softlayer.com/reference/services/SoftLayer_Product_Item_Price/
/// </manualPages>
static void Main(String [] args)
{
// You SoftLayer username
string username = "set me";
// Your SoftLayer API key.
string apiKey = "set me";
// Define the virtual guest id to place an upgrade
int virtualId = 13115425;
// Creating a connection to the SoftLayer_Product_Order API service and
// bind our API username and key to it.
authenticate authenticate = new authenticate();
authenticate.username = username;
authenticate.apiKey = apiKey;
SoftLayer_Product_OrderService orderService = new SoftLayer_Product_OrderService();
orderService.authenticateValue = authenticate;
// Build a SoftLayer_Product_Item_Price objects with the ids from prices that you want to order.
// You can retrieve them with SoftLayer_Product_Package::getItemPrices method
int[] prices = {
1645
};
List<SoftLayer_Product_Item_Price> pricesList = new List<SoftLayer_Product_Item_Price>();
foreach (var price in prices)
{
SoftLayer_Product_Item_Price newPrice = new SoftLayer_Product_Item_Price();
newPrice.id = price;
newPrice.idSpecified = true;
pricesList.Add(newPrice);
}
// Build SoftLayer_Container_Product_Order_Property object for the upgrade
SoftLayer_Container_Product_Order_Property property = new SoftLayer_Container_Product_Order_Property();
property.name = "MAINTENANCE_WINDOW";
property.value = "NOW";
List<SoftLayer_Container_Product_Order_Property> propertyList = new List<SoftLayer_Container_Product_Order_Property>();
propertyList.Add(property);
// Build SoftLayer_Virtual_Guest object with the id from vsi that you wish to place an upgrade
SoftLayer_Virtual_Guest virtualGuest = new SoftLayer_Virtual_Guest();
virtualGuest.id = virtualId;
virtualGuest.idSpecified = true;
List<SoftLayer_Virtual_Guest> virtualGuests = new List<SoftLayer_Virtual_Guest>();
virtualGuests.Add(virtualGuest);
// Build SoftLayer_Container_Product_Order object containing the information for the upgrade
//SoftLayer_Container_Product_Order orderTemplate = new SoftLayer_Container_Product_Order();
SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade orderTemplate = new SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade();
orderTemplate.containerIdentifier = "SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade";
orderTemplate.prices = pricesList.ToArray();
orderTemplate.properties = propertyList.ToArray();
orderTemplate.virtualGuests = virtualGuests.ToArray();
try
{
// We will check the template for errors, we will use the verifyOrder() method for this.
// Replace it with placeOrder() method when you are ready to order.
SoftLayer_Container_Product_Order verifiedOrder = orderService.verifyOrder(orderTemplate);
Console.WriteLine("Order Verified!");
}
catch (Exception e)
{
Console.WriteLine("Unable to place an upgrade for Virtual Guest: " + e.Message);
}
}
}
}
Let me know if this helps
Regards
I am really new to coding, never studied it or something similar, just learning it myself, never done it before, but I am trying to create my first real application right new.
However, I have some problems for 2 days which I just can't figure out, so I hope you can help me out.
Alright, so before the youtubedlCurrentWorker_Process() is created, I did define 'public string CurrentYouTubeDLVersion'.
How ever, when a button in my application executes the youtubedlCompareVersion_Process(), the CurrentYouTubeDLVersion string is empty, when it comes at the compare point.
Below is just a little part of my code.
Why is the string CurrentYouTubeDLVersion empty in the CompareVersion while the GetCurrentVersion ran before it?
Even if I double click "CurrentYouTubeDLVersion" in Visual Studio, it won't show a link to the one in the GetCurrentVersion_Process.
namespace MediaDownloader
{
public partial class updates : UserControl
{
public string LatestYoutubeDLVersion;
public string CurrentYouTubeDLVersion;
public void youtubedlGetCurrentVersion_Process()
{
if (File.Exists(YouTubeDLPath))
{
//Here I get the current version of youtube-dl.exe, to get the version number, we have to run youtube-dl.exe --version
Process youtubedl = new Process();
youtubedl.StartInfo.CreateNoWindow = true;
youtubedl.StartInfo.UseShellExecute = false;
youtubedl.StartInfo.RedirectStandardOutput = true;
youtubedl.StartInfo.RedirectStandardError = true;
youtubedl.StartInfo.FileName = YouTubeDLPath;
youtubedl.StartInfo.Arguments = " --version";
youtubedl.Start();
string CurrentYouTubeDLVersion = youtubedl.StandardOutput.ReadToEnd();
this.Dispatcher.Invoke((Action)(() =>
{
CurrentYouTubeDLVersionText.Text = "Current youtube-dl.exe version: " + CurrentYouTubeDLVersion;
YouTubeDLVersionStatusText.Text = null;
UpdateYouTubeDL.IsEnabled = false;
}));
}
public void youtubedlCompareVersion_Process()
{
youtubedlGetCurrentVersion_Process();
string LatestYoutubeDLVersion = WebClient.DownloadString("https://yt-dl.org/latest/version");
MessageBox.Show("Latest:" + LatestYoutubeDLVersion + "Current " + CurrentYouTubeDLVersion);
int YouTubeDLUptodate = CurrentYouTubeDLVersion.CompareTo(LatestYoutubeDLVersion);
if (YouTubeDLUptodate < 1)
{
YouTubeDLVersionStatusText.Text = "Your youtube-dl.exe is out of date, please click the button below to update.";
UpdateYouTubeDL.IsEnabled = true;
}
else
{
YouTubeDLVersionStatusText.Text = "youtube-dl.exe is up to date!";
UpdateYouTubeDL.IsEnabled = false;
}
}
}
Inside the youtubedlGetCurrentVersion_Process method, you're creating a new CurrentYouTubeDLVersion string, and it's completely separate from the public CurrentYouTubeDLVersion you added to the top of the class.
string CurrentYouTubeDLVersion = youtubedl.StandardOutput.ReadToEnd();
Assign to the class-level variable you made, instead of creating a new string:
CurrentYouTubeDLVersion = youtubedl.StandardOutput.ReadToEnd();
Then the value will be available to you in youtubedlCompareVersion_Process.
Take out the 'string' in front of CurrentYouTubeDLVersion and it should work
public youtubedlGetCurrentVersion_Process()
{
/* removed code to make easier to read */
//string CurrentYouTubeDLVersion = youtubedl.StandardOutput.ReadToEnd();
CurrentYouTubeDLVersion = youtubedl.StandardOutput.ReadToEnd();
/* removed code to make easier to read */
}
I have run into an issue.
I am creating a small application for working with our phone configuration files.
I have created a Class called Phone and given it 4 properties:
private int extension;
private String sExtension;
private String userName;
private String filePath;
I have included the respective get/set methods as well as:
public String Extension
{
get
{
return sExtension;
}
}
public String Path
{
get
{
return filePath;
}
}
I have created a utility class that does most of the static work. Including a method to create a List<Phone> of phone objects to populate the ListBox.
Everything works to the point of returning the List<Phone> back as the datasource for the ListBox. I have set both the:
fileList = Directory.EnumerateFiles(path, "ext-*");
lst_Files.DataSource = Utility.populatePhoneList(fileList);
lst_Files.DisplayMember = "Extension";
lst_Files.ValueMember = "Path";
The problem I am still experiencing is the ListBox is still being populated by the object name (in reference to MSDN Article)
I have read through a couple articles on this forum and most mention the same issue that I may not be calling ListBox.DisplayMember correctly but I believe I am.
Edit: I have tried returning a List<T>, ArrayList,Array[].
Edit: Code for utility
public static List<Phone> populatePhoneList(IEnumerable<String> newFileList)
{
List<Phone> phones = new List<Phone>();
Phone p = null;
for (int i = 0; i < newFileList.Count(); i++)
{
p = getPhoneInfo(newFileList.ElementAt(i));
phones.Add(p);
}
return phones;
}
public static Phone getPhoneInfo(String newPath)
{
StreamReader sr = new StreamReader(newPath);
Phone p1 = new Phone();
p1.setFilePath(newPath);
String testLine;
while (sr.Peek() >= 0)
{
testLine = sr.ReadLine();
if (testLine.Contains("reg.1.displayName"))
p1.setUserName(testLine.Substring(testLine.IndexOf("\"") + 1, ((testLine.LastIndexOf("\"") - 1) - testLine.IndexOf("\""))));
if (testLine.Contains("reg.1.address"))
p1.setExtension(testLine.Substring(testLine.IndexOf("\"") + 1, ((testLine.LastIndexOf("\"") - 1) - testLine.IndexOf("\""))));
}
return p1;
}
After digging into this problem over the weekend I started disabling some event handlers on the ListBox and found that it is working as it should be. I found that my ListBox.SelectedIndexChanged event was catching the list before the system completely populated. My solution was to turn ListBox.SelectionMode to none then reset it once the ListBox was filled.
We have a large public contacts folder in Outlook called Global Contacts, I'd like to be able to search through it and return a number of results that match certain criteria, ideally wildcard-style.
E.g. if someone puts "je" in the 'name' textbox, it will return all contacts whose names contain 'je'. This may be coupled as an AND with a companyname textbox.
Most of the examples I've seen are either in VB, or are concerned with doing this form a web app - I'm doing a winforms app, and every machine has Outlook 2002 installed (yeah, I know, update long overdue).
Can anyone point me in the right direction? Some code would be nice as a place to start.
Cheers
I ended up doing this:
Microsoft.Office.Interop.Outlook._Application objOutlook; //declare Outlook application
objOutlook = new Microsoft.Office.Interop.Outlook.Application(); //create it
Microsoft.Office.Interop.Outlook._NameSpace objNS = objOutlook.Session; //create new session
Microsoft.Office.Interop.Outlook.MAPIFolder oAllPublicFolders; //what it says on the tin
Microsoft.Office.Interop.Outlook.MAPIFolder oPublicFolders; // as above
Microsoft.Office.Interop.Outlook.MAPIFolder objContacts; //as above
Microsoft.Office.Interop.Outlook.Items itmsFiltered; //the filtered items list
oPublicFolders = objNS.Folders["Public Folders"];
oAllPublicFolders = oPublicFolders.Folders["All Public Folders"];
objContacts = oAllPublicFolders.Folders["Global Contacts"];
itmsFiltered = objContacts.Items.Restrict(strFilter);//restrict the search to our filter terms
Then just looping through itmsFiltered to add it to an ObjectListView. Hopefully this will be of use to someone else looking to do the same - it took me a while to cobble this together from various sources.
to find contacts folder you can iterate items of olFolderContacts. Here is the code
using System;
using Microsoft.Office.Interop.Outlook;
using Application = Microsoft.Office.Interop.Outlook.Application;
namespace RyanCore
{
public class Loader
{
public static ContactsViewModel LoadModel(Application objOutlook)
{
var viewModel = new ContactsViewModel();
MAPIFolder fldContacts = objOutlook.Session.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
foreach (object obj in fldContacts.Items)
{
if (obj is _ContactItem)
{
var contact = (_ContactItem) obj;
viewModel.Contacts.Add(new Contact(contact.FirstName + " " + contact.LastName, contact.Email1Address));
}
else if (obj is DistListItem)
{
var distListItem = (DistListItem) obj;
var contactGroup = new ContactGroup(distListItem.Subject);
viewModel.Groups.Add(contactGroup);
for (Int32 i = 1; i <= distListItem.MemberCount; i++)
{
Recipient subMember = distListItem.GetMember(i);
contactGroup.Contacts.Add(new Contact(subMember.Name, subMember.AddressEntry.Address));
}
}
}
return viewModel;
}
}
}