private void CompareINIs(INI file1, INI file2) { if (file1 == null || file2 == null) return; var paragraph1 = new Paragraph(); var paragraph2 = new Paragraph(); var matchedSections = new List<string>(); var missedSectionsAccountedFor = new List<string>(); foreach (var section in file1) { if (file2.ContainsKey(section.Key)) { //this section is in both INIs compare it and output matchedSections.Add(section.Key); //but first, lets take a moment to find any sections from file2 that may have been skipped at this point. Specifically sections that are not in file1 at all so we can write them out. var file2SectionKeys = file2.Keys.ToList(); var file2SectionKeysNotSeenYet = file2SectionKeys.GetRange(0, file2SectionKeys.IndexOf(section.Key)) .Where(sectionKey => !matchedSections.Contains(sectionKey) //haven't matched it before && !missedSectionsAccountedFor.Contains(sectionKey) //haven't fixed it before && !file1.ContainsKey(sectionKey)); //won't be getting to it later foreach (var missingSectionKey in file2SectionKeysNotSeenYet) { //this section is not in the first INI, display it as added if (missingSectionKey.Length > 0) { paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("[{0}]", missingSectionKey), Brushes.LightGreen, bold: true); } foreach (var keyvalue in file2[missingSectionKey]) { //none of the lines can match paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value), Brushes.LightGreen); } paragraph1.AppendLine(); paragraph2.AppendLine(); missedSectionsAccountedFor.Add(missingSectionKey); } //okay, now we continue with this section var matchedKeys = new List<string>(); var missedKeysAccountedFor = new List<string>(); if (section.Key.Length > 0) { paragraph1.AppendLine(string.Format("[{0}]", section.Key), bold: true); paragraph2.AppendLine(string.Format("[{0}]", section.Key), bold: true); } foreach (var keyvalue in section.Value) { //now check each line if (file2[section.Key].ContainsKey(keyvalue.Key)) { //this key is in both sections matchedKeys.Add(keyvalue.Key); //lets take a moment to find any keys from section2 that may have been skipped at this point. Specifically, keys are not in section1 at all, so we can write them out. var section2Keys = file2[section.Key].Keys.ToList(); var section2KeysNotSeenYet = section2Keys.GetRange(0, section2Keys.IndexOf(keyvalue.Key)) .Where(key => !matchedKeys.Contains(key) //haven't matched it before && !missedKeysAccountedFor.Contains(key) //haven't taken care of it before && !section.Value.ContainsKey(key)); //won't be getting to it foreach (var missingKey in section2KeysNotSeenYet) { paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("{0} = {1}", missingKey, file2[section.Key][missingKey]), Brushes.LightGreen); missedKeysAccountedFor.Add(missingKey); } //okay, now we continue with this new match //commented out, would only color the part of the INI line that changed, now its set to do the whole line //paragraph1.Append(string.Format("{0} = ", keyvalue.Key)); //paragraph2.Append(string.Format("{0} = ", keyvalue.Key)); if (keyvalue.Value == file2[section.Key][keyvalue.Key]) { //values are the same //paragraph1.AppendLine(string.Format("{0}", keyvalue.Value)); //paragraph2.AppendLine(string.Format("{0}", keyvalue.Value)); paragraph1.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value)); paragraph2.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value)); } else { //values are different //paragraph1.AppendLine(string.Format("{0}", keyvalue.Value), Brushes.LightGray); //paragraph2.AppendLine(string.Format("{0}", file2[section.Key][keyvalue.Key]), Brushes.LightGray); paragraph1.Append(string.Format("{0} = ", keyvalue.Key), Brushes.LightGray); paragraph2.Append(string.Format("{0} = ", keyvalue.Key), Brushes.LightGray); paragraph1.AppendLine(string.Format("{0}", keyvalue.Value), Brushes.Gray, Brushes.White); paragraph2.AppendLine(string.Format("{0}", file2[section.Key][keyvalue.Key]), Brushes.Gray, Brushes.White); } } else { //this key is only in the first section paragraph1.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value), Brushes.LightPink); paragraph2.AppendLine(); } } foreach (var keyvalue in file2[section.Key]) { if (matchedKeys.Contains(keyvalue.Key) || missedKeysAccountedFor.Contains(keyvalue.Key)) continue; //if it got here then this key is not in the first INI, display it as added //TODO: make this DRY, this exact code is used above for missing keys in-between other keys paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value), Brushes.LightGreen); missedKeysAccountedFor.Add(keyvalue.Key); //don't think it matters at this point } paragraph1.AppendLine(); paragraph2.AppendLine(); } else { //this section is not in the second INI, display it as removed if (section.Key.Length > 0) { paragraph1.AppendLine(string.Format("[{0}]", section.Key), Brushes.LightPink, bold: true); paragraph2.AppendLine(); } foreach (var keyvalue in section.Value) { //none of the lines can match paragraph1.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value), Brushes.LightPink); paragraph2.AppendLine(); } paragraph1.AppendLine(); paragraph2.AppendLine(); } } foreach (var section2 in file2) { if (matchedSections.Contains(section2.Key) || missedSectionsAccountedFor.Contains(section2.Key)) continue; //if it got here then this section is not in the first INI, display it as added //TODO: make this DRY, this exact code is used above for missing sections in-between other sections if (section2.Key.Length > 0) { paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("[{0}]", section2.Key), Brushes.LightGreen, bold: true); } foreach (var keyvalue in section2.Value) { //none of the lines can match paragraph1.AppendLine(); paragraph2.AppendLine(string.Format("{0} = {1}", keyvalue.Key, keyvalue.Value), Brushes.LightGreen); } paragraph1.AppendLine(); paragraph2.AppendLine(); missedSectionsAccountedFor.Add(section2.Key); } INI1RichTextBox.Document = new FlowDocument(paragraph1); INI2RichTextBox.Document = new FlowDocument(paragraph2); }