In my Windows Phone Mango app, I have a bunch of checkboxes, each corresponding to a day of the week. I want to filter the data I query by which checkboxes are checked. Here’s what I’ve come up with, but I feel like there’s a better solution:
Declare the checkboxes in XAML:
<CheckBox Content="Mon" x:Name="MonCheckbox" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap"/>
<CheckBox Content="Tue" x:Name="TueCheckbox" Grid.Column="1" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
<CheckBox Content="Wed" x:Name="WedCheckbox" Grid.Column="2" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
<CheckBox Content="Thur" x:Name="ThurCheckbox" Grid.Row="1" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
<CheckBox Content="Fri" x:Name="FriCheckbox" Grid.Row="1" Grid.Column="1" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
<CheckBox Content="Sat" x:Name="SatCheckbox" Grid.Row="1" Grid.Column="2" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
<CheckBox Content="Sun" x:Name="SunCheckbox" Grid.Row="2" Grid.Column="0" Checked="DayCheckbox_Tap" Unchecked="DayCheckbox_Tap" />
Associate a day with each checkbox:
public MainPage()
{
InitializeComponent();
LayoutRoot.DataContext = this;
// This is grossly imperative. Can it be done in XAML?
MonCheckbox.Tag = DayOfWeek.Monday;
TueCheckbox.Tag = DayOfWeek.Tuesday;
WedCheckbox.Tag = DayOfWeek.Wednesday;
ThurCheckbox.Tag = DayOfWeek.Thursday;
FriCheckbox.Tag = DayOfWeek.Friday;
SatCheckbox.Tag = DayOfWeek.Saturday;
SunCheckbox.Tag = DayOfWeek.Sunday;
// ...
}
Maintain a collection of the currently selected days:
ICollection<DayOfWeek> _selectedDays = new Collection<DayOfWeek>();
private void DayCheckbox_Tap(object sender, RoutedEventArgs e)
{
CheckBox checkbox = (CheckBox)sender;
if (_selectedDays.Contains((DayOfWeek)checkbox.Tag))
{
_selectedDays.Remove((DayOfWeek)checkbox.Tag);
}
else
{
_selectedDays.Add((DayOfWeek)checkbox.Tag);
}
refreshCheckinData();
}
The problem comes when I go to refresh the data that’s displayed to the user:
private void refreshCheckinData()
{
Checkins.Clear();
Checkins.AddAll(from checkin in checkinData.Items
where _selectedDays.Contains(checkin.DateTime.DayOfWeek)
select checkin);
}
public static class ExtensionMethods
{
public static void AddAll<T>(this ICollection<T> dest, IEnumerable<T> source)
{
if (dest == null)
{
throw new ArgumentNullException("dest");
}
foreach (T t in source)
{
dest.Add(t);
}
}
}
When the code tries to iterate over source in AddAll(), the following exception occurs:
Method 'Boolean Contains(System.DayOfWeek)' has no supported translation to SQL." System.Exception {System.NotSupportedException}
How can I get around this? Why does Contains require a SQL translation? Is there a better approach to this whole thing, using more declarative XAML and less imperative code-behind?
Update: I tried changing the query to:
where _selectedDays.Any(day => day == checkin.DateTime.DayOfWeek)
now I get the following error:
"Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator." System.Exception {System.NotSupportedException}
_selectedDays is defined in memory. why does it need to be translated to SQL?
Your original query is close. I think the Contains() method on ICollection is getting in the way of the SQL translator — I would need to double check the code to be sure.
You can work around this by forcing selection of the Enumerable.Contains() extension method by changing your code to this:
Enumerable.Contains(T) extension method will translate into an IN clause in the database.
The resulting query will be something like this:
SELECT [t0].[Key], [t0].[Day]
FROM [Stuff] AS [t0]
WHERE [t0].[Day] IN (@p0, @p1)