■ ItemsRepeater 엘리먼트에서 필터링/정렬 기능을 사용하는 방법을 보여준다.
▶ Recipe.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; namespace TestProject { /// <summary> /// 레시피 /// </summary> public class Recipe { //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 번호 - Number /// <summary> /// 번호 /// </summary> public int Number { get; set; } #endregion #region 명칭 - Name /// <summary> /// 명칭 /// </summary> public string Name { get; set; } #endregion #region 색상 - Color /// <summary> /// 색상 /// </summary> public string Color { get; set; } #endregion #region 재료 리스트 - IngrdientList /// <summary> /// 재료 리스트 /// </summary> public List<string> IngrdientList { get; set; } #endregion #region 재료 리스트 - Ingredients /// <summary> /// 재료 리스트 /// </summary> public string Ingredients { get; set; } #endregion #region 재료 수 - IngredientCount /// <summary> /// 재료 수 /// </summary> public int IngredientCount { get { return IngrdientList.Count(); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 재료 임의로 추가하기 - RandomizeIngredients() /// <summary> /// 재료 임의로 추가하기 /// </summary> public void RandomizeIngredients() { Random numberRandom = new Random(); Random ingredientRandom = new Random(); ObservableCollection<string> collection = new ObservableCollection<string> { "Garlic", "Lemon", "Butter", "Lime", "Feta Cheese", "Parmesan Cheese", "Breadcrumbs" }; for(int i = 0; i < numberRandom.Next(0, 4); i++) { string newIngredient = collection[ingredientRandom.Next(0, 6)]; if(!IngrdientList.Contains(newIngredient)) { Ingredients += "\n" + newIngredient; IngrdientList.Add(newIngredient); } } } #endregion } } |
▶ RecipeList.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using Microsoft.UI.Xaml.Controls; namespace TestProject { /// <summary> /// 레시피 리스트 /// </summary> public class RecipeList : IList, IKeyIndexMapping, INotifyCollectionChanged { //////////////////////////////////////////////////////////////////////////////////////////////////// Event ////////////////////////////////////////////////////////////////////////////////////////// Public #region 컬렉션 변경시 - CollectionChanged /// <summary> /// 컬렉션 변경시 /// </summary> public event NotifyCollectionChangedEventHandler CollectionChanged; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 내부 리스트 /// </summary> private List<Recipe> innerList = new List<Recipe>(); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region (IList) 카운트 - Count /// <summary> /// 카운트 /// </summary> public int Count => this.innerList != null ? this.innerList.Count : 0; #endregion #region (IList) 인덱서 - this[index] /// <summary> /// 인덱서 /// </summary> /// <param name="index">인덱스</param> /// <returns>레시피</returns> public object this[int index] { get { return this.innerList[index] as Recipe; } set { this.innerList[index] = (Recipe)value; } } #endregion #region (IList) 고정 크기 여부 - IsFixedSize /// <summary> /// 고정 크기 여부 /// </summary> public bool IsFixedSize => throw new NotImplementedException(); #endregion #region (IList) 읽기 전용 여부 - IsReadOnly /// <summary> /// 읽기 전용 여부 /// </summary> public bool IsReadOnly => throw new NotImplementedException(); #endregion #region (IList) 동기화 여부 - IsSynchronized /// <summary> /// 동기화 여부 /// </summary> public bool IsSynchronized => throw new NotImplementedException(); #endregion #region (IList) 동기 루트 - SyncRoot /// <summary> /// 동기 루트 /// </summary> public object SyncRoot => throw new NotImplementedException(); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - RecipeList(recipeEnumerable) /// <summary> /// 생성자 /// </summary> /// <param name="recipeEnumerable">레시피 열거 가능형</param> public RecipeList(IEnumerable<Recipe> recipeEnumerable) { InitializeCollection(recipeEnumerable); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 컬렉션 초기화하기 - InitializeCollection(recipeEnumerable) /// <summary> /// 컬렉션 초기화하기 /// </summary> /// <param name="recipeEnumerable">레시피 열거 가능형</param> public void InitializeCollection(IEnumerable<Recipe> recipeEnumerable) { this.innerList.Clear(); if(recipeEnumerable != null) { this.innerList.AddRange(recipeEnumerable); } CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion #region (IList) 열거자 구하기 - GetEnumerator() /// <summary> /// 열거자 구하기 /// </summary> /// <returns>열거자</returns> public IEnumerator<Recipe> GetEnumerator() => this.innerList.GetEnumerator(); #endregion #region (IList) 열거자 구하기 - IEnumerable.GetEnumerator() /// <summary> /// 열거자 구하기 /// </summary> /// <returns>열거자</returns> IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion #region (IList) 추가하기 - Add(value) /// <summary> /// 추가하기 /// </summary> /// <param name="value">값</param> /// <returns>인덱스</returns> public int Add(object value) { throw new NotImplementedException(); } #endregion #region (IList) 지우기 - Clear() /// <summary> /// 지우기 /// </summary> public void Clear() { throw new NotImplementedException(); } #endregion #region (IList) 포함 여부 구하기 - Contains(value) /// <summary> /// 포함 여부 구하기 /// </summary> /// <param name="value">값</param> /// <returns>포함 여부</returns> public bool Contains(object value) { throw new NotImplementedException(); } #endregion #region (IList) 인덱스 구하기 - IndexOf(value) /// <summary> /// 인덱스 구하기 /// </summary> /// <param name="value">값</param> /// <returns>인덱스</returns> public int IndexOf(object value) { throw new NotImplementedException(); } #endregion #region (IList) 삽입하기 - Insert(index, value) /// <summary> /// 삽입하기 /// </summary> /// <param name="index">인덱스</param> /// <param name="value">값</param> public void Insert(int index, object value) { throw new NotImplementedException(); } #endregion #region (IList) 제거하기 - Remove(value) /// <summary> /// 제거하기 /// </summary> /// <param name="value">값</param> public void Remove(object value) { throw new NotImplementedException(); } #endregion #region (IList) 제거하기 - RemoveAt(index) /// <summary> /// 제거하기 /// </summary> /// <param name="index">인덱스</param> public void RemoveAt(int index) { throw new NotImplementedException(); } #endregion #region (IList) 복사하기 - CopyTo(array, index) /// <summary> /// 복사하기 /// </summary> /// <param name="array">배열</param> /// <param name="index">인덱스</param> public void CopyTo(Array array, int index) { throw new NotImplementedException(); } #endregion #region (IKeyIndexMapping) 인덱스에서 키 구하기 - KeyFromIndex(index) /// <summary> /// 인덱스에서 키 구하기 /// </summary> /// <param name="index">인덱스</param> /// <returns>키</returns> public string KeyFromIndex(int index) { return this.innerList[index].Number.ToString(); } #endregion #region (IKeyIndexMapping) 키에서 인덱스 구하기 - IndexFromKey(key) /// <summary> /// 키에서 인덱스 구하기 /// </summary> /// <param name="key">키</param> /// <returns>인덱스</returns> public int IndexFromKey(string key) { foreach(Recipe recipe in this.innerList) { if(recipe.Number.ToString() == key) { return this.innerList.IndexOf(recipe); } } return -1; } #endregion } } |
▶ CustomVirtualizingLayout.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
using System; using System.Collections.Generic; using System.Collections.Specialized; using Windows.Foundation; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace TestProject { /// <summary> /// 커스텀 가상화 레이아웃 /// </summary> public class CustomVirtualizingLayout : VirtualizingLayout { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 첫번째 인덱스 /// </summary> private int firstIndex = 0; /// <summary> /// 마지막 인덱스 /// </summary> private int lastIndex = 0; /// <summary> /// 마지막 이용 가능한 너비 /// </summary> private double lastAvailableWidth = 0.0; /// <summary> /// 컬럼 오프셋 리스트 /// </summary> private List<double> columnOffsetList = new List<double>(); /// <summary> /// 캐시 테두리 사각형 리스트 /// </summary> private List<Rect> cachedBoundRectangleList = new List<Rect>(); /// <summary> /// 캐시 테두리 사각형 무효화 여부 /// </summary> private bool cachedBoundRectangleInvalid = false; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Public #region 너비 - Width /// <summary> /// 너비 /// </summary> public double Width { get; set; } = 150; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 항목 변경시 처리하기 (코어) - OnItemsChangedCore(context, source, e) /// <summary> /// 항목 변경시 처리하기 (코어) /// </summary> /// <param name="context">컨텍스트</param> /// <param name="source">소스</param> /// <param name="e">이벤트 인자</param> protected override void OnItemsChangedCore(VirtualizingLayoutContext context, object source, NotifyCollectionChangedEventArgs e) { this.cachedBoundRectangleList.Clear(); this.firstIndex = 0; this.lastIndex = 0; this.cachedBoundRectangleInvalid = true; InvalidateMeasure(); } #endregion #region 측정하기 (오버라이드) - MeasureOverride(context, availableSize) /// <summary> /// 측정하기 (오버라이드) /// </summary> /// <param name="context">컨텍스트</param> /// <param name="availableSize">이용 가능한 크기</param> /// <returns>측정 크기</returns> protected override Size MeasureOverride(VirtualizingLayoutContext context, Size availableSize) { Rect viewportRectangle = context.RealizationRect; if(availableSize.Width != this.lastAvailableWidth || this.cachedBoundRectangleInvalid) { UpdateCachedBoundRectangle(availableSize); this.lastAvailableWidth = availableSize.Width; } int columnCount = Math.Max(1, (int)(availableSize.Width / Width)); if(this.columnOffsetList.Count == 0) { for(int i = 0; i < columnCount; i++) { this.columnOffsetList.Add(0); } } this.firstIndex = GetStartIndex(viewportRectangle); int currentIndex = firstIndex; double nextOffset = -1.0; while(currentIndex < context.ItemCount && nextOffset < viewportRectangle.Bottom) { UIElement childElement = context.GetOrCreateElementAt(currentIndex); childElement.Measure(new Size(Width, availableSize.Height)); if(currentIndex >= cachedBoundRectangleList.Count) { int columnIndex = GetIndexOfLowestColumn(columnOffsetList, out nextOffset); this.cachedBoundRectangleList.Add ( new Rect ( columnIndex * Width, nextOffset, Width, childElement.DesiredSize.Height ) ); this.columnOffsetList[columnIndex] += childElement.DesiredSize.Height; } else { if(currentIndex + 1 == this.cachedBoundRectangleList.Count) { GetIndexOfLowestColumn(this.columnOffsetList, out nextOffset); } else { nextOffset = this.cachedBoundRectangleList[currentIndex + 1].Top; } } this.lastIndex = currentIndex; currentIndex++; } Size extentSize = GetExtentSize(availableSize); return extentSize; } #endregion #region 배열하기 (오버라이드) - ArrangeOverride(context, finalSize) /// <summary> /// 배열하기 (오버라이드) /// </summary> /// <param name="context">컨텍스트</param> /// <param name="finalSize">최종 크기</param> /// <returns>배열 크기</returns> protected override Size ArrangeOverride(VirtualizingLayoutContext context, Size finalSize) { if(this.cachedBoundRectangleList.Count > 0) { for(int index = firstIndex; index <= lastIndex; index++) { UIElement childElement = context.GetOrCreateElementAt(index); childElement.Arrange(this.cachedBoundRectangleList[index]); } } return finalSize; } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 캐시 테두리 사각형 업데이트하기 - UpdateCachedBoundRectangle(availableSize) /// <summary> /// 캐시 테두리 사각형 업데이트하기 /// </summary> /// <param name="availableSize">이용 가능한 크기</param> private void UpdateCachedBoundRectangle(Size availableSize) { int columnCount = Math.Max(1, (int)(availableSize.Width / Width)); this.columnOffsetList.Clear(); for(int i = 0; i < columnCount; i++) { this.columnOffsetList.Add(0); } for(int index = 0; index < this.cachedBoundRectangleList.Count; index++) { int columnIndex = GetIndexOfLowestColumn(this.columnOffsetList, out var nextOffset); double previousHeight = this.cachedBoundRectangleList[index].Height; this.cachedBoundRectangleList[index] = new Rect(columnIndex * Width, nextOffset, Width, previousHeight); this.columnOffsetList[columnIndex] += previousHeight; } this.cachedBoundRectangleInvalid = false; } #endregion #region 시작 인덱스 구하기 - GetStartIndex(viewportRectangle) /// <summary> /// 시작 인덱스 구하기 /// </summary> /// <param name="viewportRectangle">뷰포트 사각형</param> /// <returns>시작 인덱스</returns> private int GetStartIndex(Rect viewportRectangle) { int startIndex = 0; if(this.cachedBoundRectangleList.Count == 0) { startIndex = 0; } else { for(int i = 0; i < this.cachedBoundRectangleList.Count; i++) { Rect currentBoundRectangle = this.cachedBoundRectangleList[i]; if(currentBoundRectangle.Y < viewportRectangle.Bottom && currentBoundRectangle.Bottom > viewportRectangle.Top) { startIndex = i; break; } } } return startIndex; } #endregion #region 가장 낮은 컬럼 인덱스 구하기 - GetIndexOfLowestColumn(columnOffsetList, lowestOffset) /// <summary> /// 가장 낮은 컬럼 인덱스 구하기 /// </summary> /// <param name="columnOffsetList">컬럼 오프셋 리스트</param> /// <param name="lowestOffset">가장 낮은 오프셋</param> /// <returns>가장 낮은 컬럼 인덱스</returns> private int GetIndexOfLowestColumn(List<double> columnOffsetList, out double lowestOffset) { int lowestIndex = 0; lowestOffset = columnOffsetList[lowestIndex]; for(int index = 0; index < columnOffsetList.Count; index++) { double currentOffset = columnOffsetList[index]; if(lowestOffset > currentOffset) { lowestOffset = currentOffset; lowestIndex = index; } } return lowestIndex; } #endregion #region 범위 크기 구하기 - GetExtentSize(availableSize) /// <summary> /// 범위 크기 구하기 /// </summary> /// <param name="availableSize">이용 가능한 크기</param> /// <returns>범위 크기</returns> private Size GetExtentSize(Size availableSize) { double largestColumnOffset = this.columnOffsetList[0]; for(int index = 0; index < this.columnOffsetList.Count; index++) { double currentOffset = this.columnOffsetList[index]; if(largestColumnOffset < currentOffset) { largestColumnOffset = currentOffset; } } return new Size(availableSize.Width, largestColumnOffset); } #endregion } } |
▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?xml version="1.0" encoding="utf-8"?> <Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestProject" Title="TestProject"> <Grid Margin="10"> <Grid.Resources> <DataTemplate x:Key="RecipeDataTemplateKey" x:DataType="local:Recipe"> <StackPanel Margin="5" BorderThickness="1" Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"> <StackPanel Margin="10" Height="75" Background="{x:Bind Color}" Opacity="0.8"> <TextBlock Padding="10" Foreground="{ThemeResource SystemControlForegroundAltHighBrush}" FontSize="35" TextAlignment="Center" Text="{x:Bind Number.ToString()}" /> </StackPanel> <TextBlock Style="{StaticResource TitleTextBlockStyle}" Margin="15 0 10 0" TextWrapping="Wrap" Text="{x:Bind Name}" /> <TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15 0 15 15" Text="{x:Bind Ingredients}" /> </StackPanel> </DataTemplate> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="10" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <ItemsRepeaterScrollHost Grid.Column="0"> <ScrollViewer> <ItemsRepeater Name="itemsRepeater" ItemTemplate="{StaticResource RecipeDataTemplateKey}"> <ItemsRepeater.Layout> <local:CustomVirtualizingLayout Width="200" /> </ItemsRepeater.Layout> </ItemsRepeater> </ScrollViewer> </ItemsRepeaterScrollHost> <StackPanel Grid.Column="2"> <TextBox Name="filterTextBox" HorizontalAlignment="Left" Width="200" Header="Filter by ingredient..." /> <TextBlock Margin="0 10 0 0">Enable Animations</TextBlock> <CheckBox Name="enableAnimationCheckBox" Margin="0 10 0 0" Content="Enable animations" /> <TextBlock Margin="0 10 0 0" Text="Sort by number of ingredients" /> <Button Name="sortAscendingButton" Margin="0 10 0 0" Content="Least to most" /> <Button Name="sortDescendingButton" Margin="0 10 0 0" Content="Most to least" /> </StackPanel> </Grid> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Microsoft.UI.Xaml; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public sealed partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 소스 레시피 리스트 /// </summary> private List<Recipe> sourceRecipeList; /// <summary> /// 필터 레시피 티르스 /// </summary> private RecipeList filteredRecipeList = new RecipeList(null); /// <summary> /// 역순 정렬 여부 /// </summary> private bool isSortDescending = false; /// <summary> /// 색상 리스트 /// </summary> private List<string> colorList = new List<string>() { "Blue", "BlueViolet", "Crimson", "DarkCyan", "DarkGoldenrod", "DarkMagenta", "DarkOliveGreen", "DarkRed", "DarkSlateBlue", "DeepPink", "IndianRed", "MediumSlateBlue", "Maroon", "MidnightBlue", "Peru", "SaddleBrown", "SteelBlue", "OrangeRed", "Firebrick", "DarkKhaki" }; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); List<Recipe> sourceRecipeList = GetRecipeList(); this.filteredRecipeList.InitializeCollection(sourceRecipeList); this.sourceRecipeList = sourceRecipeList; this.itemsRepeater.ItemsSource = filteredRecipeList; this.filterTextBox.TextChanged += filterTextBox_TextChanged; this.enableAnimationCheckBox.Checked += enableAnimationCheckBox_Checked; this.enableAnimationCheckBox.Unchecked += enableAnimationCheckBox_Checked; this.sortAscendingButton.Click += sortAscendingButton_Click; this.sortDescendingButton.Click += sortDescendingButton_Click; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Event #region Filter by ingredient 텍스트 박스 텍스트 변경시 처리하기 - filterTextBox_TextChanged(sender, e) /// <summary> /// Filter by ingredient 텍스트 박스 텍스트 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void filterTextBox_TextChanged(object sender, RoutedEventArgs e) { UpdateData(); } #endregion #region Enable animations 체크 박스 체크시 처리하기 - enableAnimationCheckBox_Checked(sender, e) /// <summary> /// Enable animations 체크 박스 체크시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void enableAnimationCheckBox_Checked(object sender, RoutedEventArgs e) { #if WINUI_PRERELEASE this.itemsRepeater.Animator = this.enableAnimationCheckBox.IsChecked.GetValueOrDefault() ? new DefaultElementAnimator() : null; #endif } #endregion #region Least to most 버튼 클릭시 처리하기 - sortAscendingButton_Click(sender, e) /// <summary> /// Least to most 버튼 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void sortAscendingButton_Click(object sender, RoutedEventArgs e) { if(this.isSortDescending == true) { this.isSortDescending = false; UpdateData(); } } #endregion #region Most to least 버튼 클릭시 처리하기 - sortDescendingButton_Click(sender, e) /// <summary> /// Most to least 버튼 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void sortDescendingButton_Click(object sender, RoutedEventArgs e) { if(!this.isSortDescending == true) { this.isSortDescending = true; UpdateData(); } } #endregion //////////////////////////////////////////////////////////////////////////////// Function #region 과일 컬렉션 구하기 - GetFruitCollection() /// <summary> /// 과일 컬렉션 구하기 /// </summary> /// <returns>과일 컬렉션</returns> private ObservableCollection<string> GetFruitCollection() { return new ObservableCollection<string> { "Apricots", "Bananas", "Grapes", "Strawberries", "Watermelon", "Plums", "Blueberries" }; } #endregion #region 채소 컬렉션 구하기 - GetVegetableCollection() /// <summary> /// 채소 컬렉션 구하기 /// </summary> /// <returns>채소 컬렉션</returns> private ObservableCollection<string> GetVegetableCollection() { return new ObservableCollection<string> { "Broccoli", "Spinach", "Sweet potato", "Cauliflower", "Onion", "Brussels sprouts", "Carrots" }; } #endregion #region 곡물 컬렉션 구하기 - GetGrainCollection() /// <summary> /// 곡물 컬렉션 구하기 /// </summary> /// <returns>곡물 컬렉션</returns> private ObservableCollection<string> GetGrainCollection() { return new ObservableCollection<string> { "Rice", "Quinoa", "Pasta", "Bread", "Farro", "Oats", "Barley" }; } #endregion #region 단백질 컬렉션 구하기 - GetProteinCollection() /// <summary> /// 단백질 컬렉션 구하기 /// </summary> /// <returns>단백질 컬렉션</returns> private ObservableCollection<string> GetProteinCollection() { return new ObservableCollection<string> { "Steak", "Chicken", "Tofu", "Salmon", "Pork", "Chickpeas", "Eggs" }; } #endregion #region 레시피 리스트 구하기 - GetRecipeList() /// <summary> /// 레시피 리스트 구하기 /// </summary> /// <returns>레시피 리스트</returns> private List<Recipe> GetRecipeList() { Random random = new Random(); List<Recipe> recipeList = new List<Recipe> ( Enumerable.Range(0, 1000).Select ( k => new Recipe { Number = k, Name = "Recipe " + k.ToString(), Color = this.colorList[random.Next(0, 19)] } ) ); foreach(Recipe recipe in recipeList) { string fruitOption = GetFruitCollection()[random.Next(0, 6)]; string vegetableOption = GetVegetableCollection()[random.Next(0, 6)]; string grainOption = GetGrainCollection()[random.Next(0, 6)]; string proteinOption = GetProteinCollection()[random.Next(0, 6)]; recipe.Ingredients = "\n" + fruitOption + "\n" + vegetableOption + "\n" + grainOption + "\n" + proteinOption; recipe.IngrdientList = new List<string>() { fruitOption, vegetableOption, grainOption, proteinOption }; recipe.RandomizeIngredients(); } return recipeList; } #endregion #region 데이터 업데이트하기 - UpdateData() /// <summary> /// 데이터 업데이트하기 /// </summary> private void UpdateData() { var filteredRecipeEnumerable = sourceRecipeList.Where ( i => i.Ingredients.Contains(this.filterTextBox.Text, StringComparison.InvariantCultureIgnoreCase) ); var sortedRecipeEnumerable = this.isSortDescending ? filteredRecipeEnumerable.OrderByDescending(i => i.IngrdientList.Count()) : filteredRecipeEnumerable.OrderBy(i => i.IngrdientList.Count()); this.filteredRecipeList.InitializeCollection(sortedRecipeEnumerable); } #endregion } } |