ContactStore ContactChanged event is not firing when Modifying Contacts - c#

Modified case is never called when modifying the contact or adding a phone number
_contactStore.ContactChanged += _contactStore_ContactChanged;
private void _contactStore_ContactChanged(ContactStore sender, ContactChangedEventArgs args)
{
var defferal = args.GetDeferral();
ContactChangeReader reader = sender.ChangeTracker.GetChangeReader();
IReadOnlyList<ContactChange> changes = reader.ReadBatchAsync().AsTask().Result;
while (changes.Count != 0)
{
foreach (ContactChange change in changes)
{
switch (change.ChangeType)
{
case ContactChangeType.Modified: SomeLogic();
break;
}
}
changes = reader.ReadBatchAsync().AsTask().Result;
}
reader.AcceptChanges();

The problem may be that the ContactStoreAccessType type is incorrect. Please try use AllContactsReadWrite enumeration type.
ContactStore store = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AllContactsReadWrite);
store.ContactChanged += Store_ContactChanged;
private void Store_ContactChanged(ContactStore sender, ContactChangedEventArgs args)
{
}

Related

Using a ComboBox DropDownList to enumerate COM ports keeps losing current selection in the DropDown event due to clear()

There is a function that automatically renews the serialPort in the DropDown event. That's why we periodically clear() it. First {COM1, COM2, COM3}, then {COM1, COM2} when disconnected, maybe {COM1, COM2, COM3} when reconnected.
The problem is disappear that a previously selected value whenever an event occurs. Someone points out that it is because of the use of clear(). But, it doesn't occur this problem when DropDownStyle is DropDown.
I wrote the following code to make the comboBox1 ReadOnly.
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
And, I also have the code like this:
private void comboBox1_DropDown(object sender, EventArgs e)
{
comboBox1.Items.Clear();
// Logic to automatically add items to comboBox1
.
.
.
}
How do I should solve this problem? The key is that it can't input in comboBox other than user selecting value.
I don't make a habit of posting multiple answers on the same thread, but based on your helpful comment, it appears that your "underlying" reason for posting is wanting to keep the ComboBox up to date with COM ports that are currently connected.
There might be a more optimal solution to the thing you were trying to do in the first place (this is known as an X-Y Problem). In my old job I used to do serial port and USB enumeration quite a bit. One trick I learned along the way is that WinOS is going to post a message WM_DEVICECHANGE whenever a physical event occurs, things like plugging in or disconnecting a USB serial port device.
In a WinForms app, it's straightforward to detect it by overriding WndProc:
public MainForm() => InitializeComponent();
const int WM_DEVICECHANGE = 0x0219;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if(m.Msg == WM_DEVICECHANGE)
{
onDeviceChange();
}
}
BindingList
I would still advocate for assigning the DataSource property of the combo box to a BindingList<ComPort> where ComPort is a class we design to represent the connection info:
class ComPort
{
public string? PortName { get; set; }
public string? FriendlyName { get; set; }
public string? PnpDeviceId { get; set; }
public override string ToString() => PortName ?? "None";
public string GetFullDescription()
{
var builder = new List<string>();
builder.Add($"{PortName}");
builder.Add($"{FriendlyName}");
builder.Add($"{nameof(PnpDeviceId)}: ");
builder.Add($"{PnpDeviceId}");
return $"{string.Join(Environment.NewLine, builder)}{Environment.NewLine}{Environment.NewLine}";
}
}
Enumeration
There's a little extra work this way but the enumeration yields a descriptor with additional useful information:
BindingList<ComPort> ComPorts = new BindingList<ComPort>();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
comboBoxCom.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxCom.DropDownClosed += (sender, e) => ActiveControl = null;
comboBoxCom.DataSource = ComPorts;
ComPorts.ListChanged += onComPortChanged;
enumerateComPorts();
}
bool _isDeviceChange = false;
private void onComPortChanged(object? sender, ListChangedEventArgs e)
{
if(_isDeviceChange) BeginInvoke(() => richTextBox.Clear());
ComPort comPort;
switch (e.ListChangedType)
{
case ListChangedType.ItemAdded:
comPort = ComPorts[e.NewIndex];
using (ManagementClass manager = new ManagementClass("Win32_PnPEntity"))
{
foreach (ManagementObject record in manager.GetInstances())
{
var pnpDeviceId = record.GetPropertyValue("PnpDeviceID")?.ToString();
if (pnpDeviceId != null)
{
var subkey = Path.Combine("System", "CurrentControlSet", "Enum", pnpDeviceId, "Device Parameters");
var regKey = Registry.LocalMachine.OpenSubKey(subkey);
if (regKey != null)
{
var names = regKey.GetValueNames();
if (names.Contains("PortName"))
{
var portName = regKey.GetValue("PortName");
if (Equals(comPort.PortName, portName))
{
var subkeyParent = Path.Combine("System", "CurrentControlSet", "Enum", pnpDeviceId);
var regKeyParent = Registry.LocalMachine.OpenSubKey(subkeyParent);
comPort.FriendlyName = $"{regKeyParent?.GetValue("FriendlyName")}";
comPort.PnpDeviceId = pnpDeviceId;
}
}
}
}
}
}
break;
case ListChangedType.ItemDeleted:
comPort = _removedItem!;
break;
default: return;
}
BeginInvoke(() =>
{
if (_isDeviceChange)
{
richTextBox.SelectionColor =
e.ListChangedType.Equals(ListChangedType.ItemAdded) ?
Color.Green : Color.Red;
richTextBox.AppendText($"{e.ListChangedType}{Environment.NewLine}");
}
else
{
richTextBox.SelectionColor = Color.Blue;
richTextBox.AppendText($"Detected{Environment.NewLine}");
}
richTextBox.SelectionColor = Color.FromArgb(0x20, 0x20, 0x20);
richTextBox.AppendText(comPort?.GetFullDescription());
});
}
int _wdtCount = 0;
private ComPort? _removedItem = null;
private void onDeviceChange()
{
_isDeviceChange = true;
int captureCount = ++_wdtCount;
Task
.Delay(TimeSpan.FromMilliseconds(500))
.GetAwaiter()
.OnCompleted(() =>
{
if(captureCount.Equals(_wdtCount))
{
// The events have settled out
enumerateComPorts();
}
});
}
private void enumerateComPorts()
{
string[] ports = SerialPort.GetPortNames();
foreach (var port in ports)
{
if (!ComPorts.Any(_ => Equals(_.PortName, port)))
{
ComPorts.Add(new ComPort { PortName = port });
}
}
foreach (var port in ComPorts.ToArray())
{
if (!ports.Contains(port.PortName))
{
_removedItem = port;
ComPorts.Remove(port);
}
}
BeginInvoke(()=> ActiveControl = null); // Remove focus rectangle
}
The DropDown event of the combo box is now no longer required. Problem solved!
Here is an alternative to using Clear.
One solution is to have a binding list that is the data source of your ComboBox. When you enumerate the available COM ports (shown below as a simulation) you will add or remove com ports from the binding source based on availability. This should result in the behavior you want, because if a com port is "still" available the selection won't change.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
comboBoxCom.DropDownStyle= ComboBoxStyle.DropDownList;
comboBoxCom.DataSource = MockDataSource;
refreshAvailableComPorts(init: true);
comboBoxCom.DropDown += onComboBoxComDropDown;
}
BindingList<string> MockDataSource = new BindingList<string>();
private void onComboBoxComDropDown(object? sender, EventArgs e)
{
refreshAvailableComPorts(init: false);
}
/// <summary>
/// This is a simulation that will choose a different
/// set of "available" ports on each drop down.
/// </summary>
private void refreshAvailableComPorts(bool init)
{
if(init)
{
MockDataSource.Clear();
MockDataSource.Add("COM2");
}
else
{
string
selB4 = string.Join(",", MockDataSource),
selFtr;
do
{
var flags = _rando.Next(1, 0x10);
if ((flags & 0x1) == 0) MockDataSource.Remove("COM1");
else if(!MockDataSource.Contains("COM1")) MockDataSource.Add("COM1");
if ((flags & 0x2) == 0) MockDataSource.Remove("COM2");
else if (!MockDataSource.Contains("COM2")) MockDataSource.Add("COM2");
if ((flags & 0x4) == 0) MockDataSource.Remove("COM3");
else if (!MockDataSource.Contains("COM3")) MockDataSource.Add("COM3");
if ((flags & 0x8) == 0) MockDataSource.Remove("COM4");
else if (!MockDataSource.Contains("COM4")) MockDataSource.Add("COM4");
selFtr = string.Join(",", MockDataSource);
if (!selFtr.Equals(selB4))
{
break;
}
} while (true);
}
}
private Random _rando = new Random(5);
}

Using GetActiveTcpConnections to continuously check for new IP

I"m new to C# and am looking to see if the way I'm using this code to check for a new connection in a background worker is safe and or a better why to do it. My end goal is to run this as a service but, I'm using the background worker as a way to get real-time updates for testing. The code works but I don't know if its safe/stable or the best way to do it?
Any help would be appreciated.
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync(2000);
MessageBox.Show("Started backgroung worker");
}
private void button2_Click(object sender, EventArgs e)
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
}
MessageBox.Show("Stopped backgroung worker");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker helperBW = sender as BackgroundWorker;
e.Result = BackgroundProcessLogicMethod(helperBW, arg);
if (helperBW.CancellationPending)
{
e.Cancel = true;
}
}
private int BackgroundProcessLogicMethod(BackgroundWorker bw, int a)
{
while (true)
{
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in connections)
{
if (c.RemoteEndPoint.ToString() == "192.168.4.14:443")
richTextBox1.Invoke(new Action( () => richTextBox1.AppendText(c.RemoteEndPoint.ToString() + "\n\r") ) );
}
}
}
Consider throttling the while(true)... loop but other than that you seem to be doing the right things in your post. This kind of thing shows up a lot in my own production code so I wanted to offer the tried-and-true approach that works for me. It's just another perspective, however.
Your code references RichTextBox which makes me infer that a WinForms example will fly here, but the general approach is valid regardless of platform anyway.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
checkBoxRun.CheckedChanged += onRunChanged;
}
CancellationTokenSource _cts = null;
private async void onRunChanged(object sender, EventArgs e)
{
if (checkBoxRun.Checked)
{
_cts = new CancellationTokenSource();
while (checkBoxRun.Checked)
{
richTextBox1.AppendTextOnUIThread(DateTime.Now.ToString());
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in connections)
{
var endpoint = c.RemoteEndPoint.ToString();
switch (endpoint)
{
case "192.168.4.14:443":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Red);
break;
case "127.0.0.1:49813":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Blue);
break;
case "127.0.0.1:55846":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Green);
break;
default:
continue;
}
}
richTextBox1.AppendTextOnUIThread();
// Throttle the polling to a reasonable repeat rate.
var task = Task.Delay(TimeSpan.FromSeconds(1), _cts.Token);
try{ await task; } catch (TaskCanceledException){ Debug.WriteLine("Cancelled"); }
}
}
else _cts.Cancel();
}
}
With the following extension for RichTextBox:
static class Extensions
{
public static void AppendTextOnUIThread(this RichTextBox #this, string text = "", bool newLine = true, Color? color = null)
{
if (!#this.Disposing)
{
if (newLine) text = $"{text}{Environment.NewLine}";
#this.BeginInvoke(new Action(() =>
{
if (color == null)
{
#this.AppendText(text);
}
else
{
var colorB4 = #this.ForeColor;
#this.SelectionColor = (Color)color;
#this.AppendText(text);
#this.SelectionColor = colorB4;
}
}));
}
}
}

Reading out a C# dll from VB6

I have a C# Class Library, that reads Bluetooth LE Characteristics. I want to use this DLL to get the read values in my VB6 app.
My Problem is: I need the complete code to get the result. That means i have to call the main function from my VB6 program, but i have no idea what parameters i have to use and how i can get the right return value.
My Dll:
using SDKTemplate;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Storage.Streams;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;
using System.Runtime.InteropServices;
namespace BleSirLo
{
public class Program
{
public byte[] ret = new byte[6];
static void Main(string[] args)
{
Task t = MainAsync(args);
t.Wait();
// or, if you want to avoid exceptions being wrapped into AggregateException:
// MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
Program p = new Program();
p.StartBleDeviceWatcher();
}
private List<DeviceInformation> UnknownDevices = new List<DeviceInformation>();
private List<DeviceInformation> _knownDevices = new List<DeviceInformation>();
private IReadOnlyList<GattCharacteristic> characteristics;
private IReadOnlyList<GattDeviceService> services;
private GattDeviceService currentSelectedService = null;
private GattCharacteristic currentSelectedCharacteristic = null;
private DeviceWatcher deviceWatcher;
public bool done = false;
private void StartBleDeviceWatcher()
{
// Additional properties we would like about the device.
// Property strings are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/ff521659(v=vs.85).aspx
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected", "System.Devices.Aep.Bluetooth.Le.IsConnectable" };
// BT_Code: Example showing paired and non-paired in a single query.
string aqsAllBluetoothLEDevices = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
deviceWatcher =
DeviceInformation.CreateWatcher(
aqsAllBluetoothLEDevices,
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
// Register event handlers before starting the watcher.
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
// Start over with an empty collection.
_knownDevices.Clear();
//deviceWatcher.Stop();
deviceWatcher.Start();
while(true)
if (done == true)
break;
}
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
{
//Debug.WriteLine(String.Format("Device Found!" + Environment.NewLine + "ID:{0}" + Environment.NewLine + "Name:{1}", deviceInfo.Id, deviceInfo.Name));
//notify user for every device that is found
if (sender == deviceWatcher)
{
if ((deviceInfo.Name == "Ei Gude, wie?") || (deviceInfo.Name == "Ei Gude, Wie?"))
{
sender.Stop();
ConnectDevice(deviceInfo);
}
}
}
private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfo)
{
}
private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfo)
{
}
private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
}
private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
}
//trigger StartBleDeviceWatcher() to start bluetoothLe Operation
private void Scan()
{
//empty devices list
_knownDevices.Clear();
UnknownDevices.Clear();
//finally, start scanning
StartBleDeviceWatcher();
}
private async void ConnectDevice(DeviceInformation deviceInfo)
{
//get bluetooth device information
BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
//Respond(bluetoothLeDevice.ConnectionStatus.ToString());
//get its services
GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();
//verify if getting success
if (result.Status == GattCommunicationStatus.Success)
{
//store device services to list
services = result.Services;
//loop each services in list
foreach (var serv in services)
{
//get serviceName by converting the service UUID
string ServiceName = Utilities.ConvertUuidToShortId(serv.Uuid).ToString();
//if current servicename matches the input service name
if (ServiceName == "65520") //ServiceTxtBox.Text)
{
//store the current service
currentSelectedService = serv;
//get the current service characteristics
GattCharacteristicsResult resultCharacterics = await serv.GetCharacteristicsAsync();
//verify if getting characteristics is success
if (resultCharacterics.Status == GattCommunicationStatus.Success)
{
//store device services to list
characteristics = resultCharacterics.Characteristics;
//loop through its characteristics
foreach (var chara in characteristics)
{
//get CharacteristicName by converting the current characteristic UUID
string CharacteristicName = Utilities.ConvertUuidToShortId(chara.Uuid).ToString();
//if current CharacteristicName matches the input characteristic name
if (CharacteristicName == "65524")//CharacteristicsTxtBox.Text)
{
//store the current characteristic
currentSelectedCharacteristic = chara;
//stop method execution
readBuffer();
return;
}
}
}
}
}
}
}
//function that handles the read button event
private async void readBuffer()
{
{
if (currentSelectedService != null && currentSelectedCharacteristic != null)
{
GattCharacteristicProperties properties = currentSelectedCharacteristic.CharacteristicProperties;
//if selected characteristics has read property
if (properties.HasFlag(GattCharacteristicProperties.Read))
{
//read value asynchronously
GattReadResult result = await currentSelectedCharacteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
int a, b, c, d, e, f;
var reader = DataReader.FromBuffer(result.Value);
//byte [] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(ret);
a = ret[0];
b = ret[1];
c = ret[2];
d = ret[3];
e = ret[4];
f = ret[5];
Ret_val(ret);
}
}
}
}
}
private void Response_TextChanged(object sender, EventArgs e)
{
}
}
In my VB6 program I call the Library like this: (It is obviously not working, but i dont know how i should do it.
Dim WithEvents CSharpInteropServiceEvents As CSharpInteropService.LibraryInvoke
Dim load As New LibraryInvokeparam(0) = Me.hwnd
Dim sa as variant
Set CSharpInteropServiceEvents = load
sa = load.GenericInvoke("C:\Program Files (x86)\Microsoft Visual Studio\VB98\WINCOM\BleSirLo.dll", "BleSirLo.Program", "Main", param)

Different behaviors when instantiated from button or timer in c#

I have a function called getMessages that can be called by a Button click (using the RelayCommand trigger) or that is called in a timer every 15s.
The desired behavior is:
webservice > deserialize answer > system notification > updatelistview > insert localDB
But when the function is called by the timer the updatelistview is not done. Why does this happen if the function is the same and works perfectly in the button command?
CODE:
// Get messages for the logged in user
public async void getMessages()
{
try
{
List<FriendGetMessage> msg = new List<FriendGetMessage>();
var response = await CommunicationWebServices.GetCHAT("users/" + au.idUser + "/get", au.token);
if (response.StatusCode == HttpStatusCode.OK) // If there are messages for me.
{
var aux = await response.Content.ReadAsStringAsync();
IEnumerable<FriendGetMessage> result = JsonConvert.DeserializeObject<IEnumerable<FriendGetMessage>>(aux);
if (result != null)
{
foreach (var m in result)
{
msg.Add(m);
}
//MsgList=msg;
foreach (var f in Friends)
{
if (f.msg == null || f.msg.Count() == 0)
{
f.msg = new ObservableCollection<Messages>();
}
foreach (var mess in msg)
{
if (mess.idUser == f.idUser)
{
Messages mm = new Messages();
mm.received = mess.message;
mm.timestamp = "Received " + mess.serverTimestamp;
mm.align = "Right";
// Add to the friend list.
f.msg.Add(mm);
// Add to Local DB
InsertMessage(null, au.idUser.ToString(), f.idUser, mess.message, mess.serverTimestamp);
var notification = new System.Windows.Forms.NotifyIcon()
{
Visible = true,
Icon = System.Drawing.SystemIcons.Information,
BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info,
BalloonTipTitle = "New Message from " + f.name,
BalloonTipText = "Message: " + mess.message,
};
// Display for 5 seconds.
notification.ShowBalloonTip(5);
// The notification should be disposed when you don't need it anymore,
// but doing so will immediately close the balloon if it's visible.
notification.Dispose();
}
}
}
counterChat = 1; // resets the counter
}
}
else {
counterChat = counterChat * 2;
}
//var sql = "select * from chat";
//var respo = GetFromDatabase(sql);
OnPropertyChanged("Friends");
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
Debug.WriteLine("{0} Exception caught.", e);
}
}
CODE TIMER:
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
ADDED - I've also tried this and didn't worked (it seems that is some kind of concurrency problem to the ObservableCollection called Friends (is a friendslist) each friend has an ObservableCollection of messages (is a chat))
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
Application.Current.Dispatcher.Invoke((Action)async delegate { await getMessages(); });
incChat = 0;
}
}
Best regards,
I think you need to make the timer handler be an async method as follows:
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
await getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
This way OnPropertyChanged("Friends") is guaranteed to fire after the work in getMessages is done.
The methods need to change to:
DispatcherTimer _timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
public async void timerchat_Tick(object sender, EventArgs e)
{
//...
await getMessages();
//...
}
public async Task getMessages()
{
try
{
// ... your code here
string result = await response.Content.ReadAsStringAsync();
// .... rest of your code
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
}
}
It is solved. The problem was in my ViewModels I was opening multiple threads and sometimes the right one would update the UI and sometimes no.
Thanks for all the answers.

SearchBox result suggestion Windows 8.1

I got a strange issue with my searchBox in Windows 8.1 App.
I got an unhandler exception (and a crush) if in my Suggestion i do not append the querySuggestion and append only the ResultSuggestion.
the problem occurs when i change the queryText.
This is my function
public async void OnSuggest(Windows.UI.Xaml.Controls.SearchBox e, SearchBoxSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
var queryText = args.QueryText != null ? args.QueryText.Trim() : null;
if (string.IsNullOrEmpty(queryText)) return;
TransporterExt tr_search = new TransporterExt();
tr_search.name = queryText;
try
{
var suggestionCollection = args.Request.SearchSuggestionCollection;
ObservableCollection<TransporterExt> querySuggestions = await TransporterService.Search(tr_search);
if (querySuggestions != null && querySuggestions.Count > 0)
{
foreach (TransporterExt tr in querySuggestions)
{
//if (tr.name.ToUpperInvariant().Contains(e.QueryText.ToUpperInvariant()))
//{
// //suggestionCollection.AppendQuerySuggestion(tr.name);
// suggestionCollection.AppendResultSuggestion(tr.name,
// tr.trId.ToString(),
// tr.trId.ToString(),
// imgRef, "imgDesc");
//}
suggestionCollection.AppendQuerySuggestion(tr.name);
}
}
}
catch (Exception)
{
//Ignore any exceptions that occur trying to find search suggestions.
}
deferral.Complete();
}
I got the searchBox inside an UserControl
My controller code
public delegate void SuggestionsRequested(Windows.UI.Xaml.Controls.SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args);
public event Windows.Foundation.TypedEventHandler<Windows.UI.Xaml.Controls.SearchBox, SearchBoxSuggestionsRequestedEventArgs> SearchBoxSuggestionsRequested;
private void SearchBoxSuggestions(Windows.UI.Xaml.Controls.SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
if (SearchBoxSuggestionsRequested != null)
SearchBoxSuggestionsRequested(sender, args);
}
I got this exception
WinRT: A method was called at an unexpected time.
exception: System.InvalidOperationException - type (string)
Edited Solution - Working function
First of all i remove from the constructor of the page the registration of event
public TruckCrudPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
this.navigationHelper.SaveState += navigationHelper_SaveState;
//this.truckForm.SearchBoxSuggestionsRequested += OnSuggest;
}
public async void OnSuggest(Windows.UI.Xaml.Controls.SearchBox e, SearchBoxSuggestionsRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
TransporterExt tr_search = new TransporterExt();
ObservableCollection<TransporterExt> querySuggestions = new ObservableCollection<TransporterExt>();
var queryText = args.QueryText != null ? args.QueryText.Trim() : null;
if (string.IsNullOrEmpty(queryText)) return;
suggested.Clear();
tr_search.name = queryText;
try
{
var suggestionCollection = args.Request.SearchSuggestionCollection;
querySuggestions = await TransporterService.Search(tr_search);
if (querySuggestions != null && querySuggestions.Count > 0 )
{
int i = 0;
foreach (TransporterExt tr in querySuggestions)
{
if (tr.name.StartsWith(e.QueryText, StringComparison.CurrentCultureIgnoreCase))
//if (tr.name.ToLower().Contains(e.QueryText))
{
string name = tr.name;
string detail = tr.trId.ToString();
string tag = i.ToString();
string imageAlternate = "imgDesc";
suggestionCollection.AppendResultSuggestion(name, detail, tag, imgRef, imageAlternate);
suggested.Add(tr);
//Debug.WriteLine("dentro" + suggested.Count);
i++;
}
}
}
}
catch (Exception exc)
{
//Ignore any exceptions that occur trying to find search suggestions.
Debug.WriteLine("Exception generata " + exc.Message);
Debug.WriteLine(exc.StackTrace);
}
deferral.Complete();
}
But it works only with condition StartsWith and i would like to use Contains
You can use SearchBox and SuggestionRequested event to fire the event when type on the SearchBox. I will show an Example
<SearchBox x:Name="SearchBoxSuggestions" SuggestionsRequested="SearchBoxEventsSuggestionsRequested"/>
and write the SearchBoxEventsSuggestionsRequested handler in the code behind
private void SearchBoxEventsSuggestionsRequested(object sender, SearchBoxSuggestionsRequestedEventArgs e)
{
string queryText = e.QueryText;
if (!string.IsNullOrEmpty(queryText))
{
Windows.ApplicationModel.Search.SearchSuggestionCollection suggestionCollection = e.Request.SearchSuggestionCollection;
foreach (string suggestion in SuggestionList)
{
if (suggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))
{
suggestionCollection.AppendQuerySuggestion(suggestion);
}
}
}
}
You can add the keyword to SuggestioList, and it will show in the dropdown when you type on the Searchbox.
Create the SuggestionList
public List<string> SuggestionList { get; set; }
initialize the list
SuggestionList = new List<string>();
and add keywords to the list
SuggestionList.Add("suggestion1");
SuggestionList.Add("suggestion2");
SuggestionList.Add("suggestion3");
SuggestionList.Add("suggestion4");
SuggestionList.Add("Fruits");
When you type s on the Searchbox it will show all the keyword starts with s.
Thanks.

Categories

Resources